介绍
当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过 新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,所有线程都处于等待分配 CPU 时间片,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。
新建状态(NEW)
当程序使用new关键字创建一个线程后,该线程处于新建状态,此时的线程情况如下:
JVM 为其分配内存,并初始化成员变量的值;
线程对象没有表现出任何线程的动态特征,程序不会执行线程的执行体。
就绪状态(RUNNABLE)
当线程对象调用了 start()
方法后,该线程就处于就绪状态,此时线程等待系统分配CPU时间片,获取到时间片之后才能执行。
直接调用
run()
方法,这个方法会立即执行,且在run()
方法返回之前其他线程无法并发执行。 调用了run()
方法后不能再调用start()
方法,否则抛出IllegaIThreadStateExccption
异常。
运行状态(RUNNING)
阻塞状态(BLOCKED)
处于运行状态的线程在某些情况下,让出自己的CPU时间片,暂停自己的运行,进入阻塞状态。
以下情况会发生阻塞:
- 调用
sleep()
方法。 - 调用一个阻塞式IO方法,在该方法返回之前,线程会被阻塞。
- 试图获得一个同步监视器,但该同步监视器被线程持有。
- 调用
suspend()
方法。 -
等待状态(WAITING)
线程处于无限制等待状态,等待一个特殊事件重新唤醒。
以下操作可以触发唤醒: 调用
notity()
或notifyAll()
方法唤醒调用wait()
方法的线程。-
wait()和notify()或notifyAll()需要搭配synchronized关键字使用
从语义角度来讲, 一个线程调用了 wait() 之后, 必然需要由另外一个线程调用 notify() 来唤醒该线程, 所以本质上, wait() 与 notify() 的成对使用, 是一种线程间的通信手段。
进一步分析, wait() 操作的调用必然是在等待某种条件的成立, 而条件的成立必然是由其他的线程来完成的。 所以实际上, 我们调用 wait() 的时候, 实际上希望达到如下的效果:// 线程A 的代码
while(!condition){ // 不能使用 if , 因为存在一些特殊情况, 使得线程没有收到 notify 时也能退出等待状态
wait();
}
// do something
// 线程 B 的代码
if(!condition){
// do something ...
condition = true;
notify();
}
现在考虑, 如果wait() 和 notify() 的操作没有相应的同步机制, 则会发生如下情况:
第一,【线程A】 进入了 while 循环后(通过了 !condition 判断条件, 但尚未执行 wait 方法), CPU 时间片耗尽, CPU 开始执行线程B的代码。
第二,【线程B】 执行完毕了 condition = true; notify(); 的操作, 此时【线程A】的 wait() 操作尚未被执行, notify() 操作没有产生任何效果。
第三,【线程A】执行wait() 操作, 进入等待状态,如果没有额外的 notify() 操作, 该线程将持续在 condition = true 的情形下, 持续处于等待状态得不到执行。
由此看出, 在使用 wait() 和 notify() 这种会挂起线程的操作时, 我们需要一种同步机制保证, condition 的检查与 wait() 操作, 以及 condition 的更新与 notify() 是互斥的。
正确的用法如下:// 线程 A 的代码
synchronized(obj_A)
{
while(!condition){
obj_A.wait();
}
// do something
}
// 线程 B 的代码
synchronized(obj_A)
{
if(!condition){
// do something ...
condition = true;
obj_A.notify();
}
}
限时等待(TIMED_WAITING)
死亡状态(DEAD)
以下方式说明线程处于死亡状态:
run()
或call()
方法执行完成,正常结束。- 线程抛出一个未捕获的 Exception 或 Error。
- 直线调用 stop() 方法,不推荐使用。
线程执行流程图
参考资料
Java线程的生命周期