Java线程内容
一:线程的创建
- 继承Thread类 重写run方法
public class MyTest {
public static void main(String[] args) {
MyJob mj = new MyJob();
mj.start();
}
}
class MyJob extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyJob:" + i);
}
}
}
- 实现Runnable接口 重写run方法(用的最多的)
public class MiTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable);
t1.start();
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("MyRunnable:" + i);
}
}
}
最常用的方式是
匿名内部类方式:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("thread:" + i);
}
}
}).start();;
lambda方式:
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("thread:" + i);
}
}).start();
- 实现Callable接口 重写call方法,配合FutureTask
特点:可以返回结果
public class MiTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCallable myCallable = new MyCallable();
FutureTask futureTask = new FutureTask(myCallable);
//创建Thread线程
Thread t1 = new Thread(futureTask);
//启动线程
t1.start();
//获取结果
Object count = futureTask.get();
System.out.println("返回结果:" + count);
}
}
class MyCallable implements Callable{
@Override
public Object call() throws Exception {
int count = 0;
for (int i = 0; i < 10; i++) {
count += i;
}
return count;
}
}
- 基于线程池构建线程
二:Java中线程的6种状态
说明:
NEW(新建状态):Thread对象被创建出来了,但是还没有执行start方法。
RUNNABLE(就绪状态):Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
BLOCKED(阻塞状态):synchronized没有拿到同步锁,被阻塞的情况
WAITING(等待状态):调用wait方法就会处于WAITING状态,需要被手动唤醒
TIME_WAITING(超时等待):调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒
TERMINATED(结束状态):run方法执行完毕,线程生命周期到头了
BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程
三:线程内容
线程的强占:
使用Thread类的非静态方法join(),join() 方法允许一个线程等待另一个线程完成
join方法可以指定等待时间,如果子线程在等待时间内结束,则主线程自动变为就绪状态
默认情况下,线程都是非守护线程
JVM会在程序中没有非守护线程时,结束掉当前JVM。主线程默认是非守护线程,如果主线程执行结束,需要查看当前JVM内是否还有非守护线程,如果没有JVM直接停止。当对象被回收的时候,会执行finalize方法,执行finalize方法的线程是守护线程,所以不一定会执行成功。
在调用wait方法和notify以及norifyAll方法时,必须在synchronized修饰的代码块或者方法内部才可以,因为要操作基于某个对象的锁的信息维护。
线程调用wait方法后,当前线程会释放锁资源
线程的结束方式:
- 让线程的run方法执行结束,无论是return结束,还是抛出异常结束,都可以的
- 使用interrupt方法
使用共享变量方式,改变interrupt标记位,线程默认情况下标记位是false的(不常用)
通过打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true){
// 执行任务
// 没有任务,让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("基于打断形式结束当前线程");
return;
}
}
});
t1.start();
Thread.sleep(500);
t1.interrupt();
}
四、Java并发编程三大特性:
1> 原子性:原子性指一个操作是不可分割的,不可中断的,一个线程在执行时,另一个线程不会影响到他。
问:如何保证原子性呢?
使用synchronized可以让避免多线程同时操作临界资源,同一时间点,只会有一个线程正在操作临界资源
使用Lock锁,推荐ReentrantLock锁,性能会更好,ReentrantLock底层是基于AQS实现的,有一个基于CAS维护的state变量来实现锁的操作。
使用ThreadLocal,他保证原子性的方式,是不让多线程去操作临界资源,让每个线程去操作属于自己的数据
ThreadLocal内存泄漏问题:
- 如果ThreadLocal引用丢失,key因为弱引用会被GC回收掉,如果同时线程还没有被回收,就会导致内存泄漏,内存中的value无法被回收,同时也无法被获取到。
- 只需要在使用完毕ThreadLocal对象之后,及时的调用remove方法,移除Entry即可
2> 可见性
3> 有序性