操作系统的线程生命周期
初始状态,可运行状态,运行状态,终止状态,休眠状态,他们之间的转化关系如图:
- 初始状态:线程被创建,但是还未分配cpu执行的状态。
- 可运行状态:指线程可以分配cpu执行。
- 运行状态:有cpu空闲时候分配cpu执行,当线程被分配给cpu执行时候,变为可执行状态。
- 终止状态:线程执行完成或者异常终止。
- 休眠状态:线程调用阻塞的api或者等待某个事件执行的时候会变为休眠状态。
jvm将由于将线程的调度交给操作系统,所以将可运行状态和运行状态和在了一起,因为jvm不关心这俩个状态的具体情况。
java线程的生命周期
NEW(初始装填)、RUNABLE(可运行/运行状态)、TERMINATED(终止状态)、BLOCK(阻塞)、WAIT(等待)、TIME_WAIT(有时限等待)
其中BLOCK、WAIT、TIME_WAIT都属于休眠关系
- NEW:当线程对象被创建出来的时候线程处于该状态,这时候还未对其分配cpu。具体方法是集成Thread类并且实现run方法,或者狗仔一个Thread对象把一个Runnable的接口实现传进去。
- NEW–>RUNABLE:调用thread.start()方法线程变为RUNABLE状态。
- RUNABLE–>休眠状态:java的休眠状态有三种BLOCK、WAIT、TIME_WAIT,分别是
- RUNABLE–>BLOCK:当遇到synchronized代码块时候,如果竞争失败当前线程进入block状态。等待进入到线程又会变为RUNABLE状态,注意在java中如调用到了阻塞的api,是不会切换到block状态的,比如调用io的api,当前线程依然是RUNABLE状态这点要注意。
- RUNABLE–>WAIT:如下3中情况会让当前线程进入到wait状态
- object.wait(),没有时间限制的wait,需要调用object.notify()或者object.notifyAll()
- 调用了其他线程的join(),没有时间限制的join,需要等待当前join的线程终止。
- 调用了LockSupport.park(),没有事件限制的park,对应的方法是LockSupport.unPark()
- RUNABLE–>TIME_WAIT:同上就是上面3种情况或者方法的有时间版本
- RUNABLE–>TERMINATED:
- 线程执行结束
- stop()或者interrupt()方法
线程终止的方法
当一个线程执行一个很耗时的逻辑时候,可能会终止该线程的操作,比如并发情况下的网络超时,为了防止资源耗尽往往要终止。上面提到了stop或者interrupt方法
- stop方法已经不建议使用了,因为会立即杀死线程,同时不会释放synchronized的锁,所以后面的线程都会阻塞。
interrupt方法相对来说就会“温柔”很多,它只是对线程了interrupt标记,当调用thread.interrupt()方法有俩种方式退出线程
- 主动退出:对于一个一直执行的线程来说可以通过判断t1.isInterrupted()来判断是否该终端
1
2
3
4
5final Thread t1 = new Thread(() -> {
while(!t1.isInterrupted()){
//do something
}
});- 异常退出:对于执行了wait sleep这种线程我们可以通过异常捕获来判断线程的终端,因为他们会抛出InterruptedException异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14final Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
//do something
//这时候会输出false因为interrupt会被清除
System.out.println(t1.isInterrupted());
}
});
注意:当抛出InterruptedException异常后线程的interrupt标记会被清楚如果这时候在判断isInterrupted()方法就又变成了false