1.疑惑:Java的线程究竟有几种状态?
在Java的并发编程技术中,经常会看到Java线程的状态迁移图。
- 有的这样画:
- 有的这样画:
- 有的这样画:
笔者刚接触Java时,看到形形色色的文章和图例,比较大的困扰是Java的线程究竟有几种状态?
2.探索1:官方文档
在Java8,Thread的状态定义在
java.lang.Thread.State
中。Java线程的状态有6种:- NEW
- RUNNABLE
- BLOCKED
- WATING
- TIMED_WAITING
- TERMINATED
详见:https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.State.html
- 在Java20(Java最新版本),Thread的状态定义在
java.lang.Thread.State
中**。Java线程的状态依然是6种**。
详见:https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/Thread.State.html
- 结论:从官方文档中看,Java线程的状态为6种:
- NEW
- RUNNABLE
- BLOCKED
- WATING
- TIMED_WAITING
- TERMINATED
3.探索2:JDK源码
- 在Java8中,Java线程状态位于
java/lang/Thread.java
的State
枚举类:
- NEW:当Java线程处于此状态时,创建线程那一刻,线程的状态。
- RUNNABLE:当Java线程处于此状态时,JVM有可能去执行它(此处埋有伏笔)。
- BLOCKED:此状态,与
synchronized
、Object.wait
强相关(后文代码演示)。
- WAITING:此状态与
BLOCKED
一样,与Object.wait
、Thread.join
等相关(后文代码演示)。
- TIMED_WAITING:与
WAITING
类似的一种状态,与Thread.sleep
、Object.wait
、Thread.join
等有关(后文代码演示)。
- TERMINATED:终结态,没啥好说的。
4.思考:为什么对Java线程的状态描述不同?
无论从官方文档,还是JDK源码,都很容易看到Java线程只有6种状态。
那么,为什么各种资料中对Java线程的状态描述不同呢?
我认为,可能是视角不同:
- 笔者在之前的文章中,分析过JVM启动时会产生多少线程,即我们到底在操作系统层面看线程,还是在JVM层面看线程。
- 站在JVM层面,Java给Java程序猿看到
创建Java线程
、Java线程有6种状态
等等概念。 - 站在OS层面,JVM为了实现Java程序猿看到的
Java线程
,调用了pthread
这类库,创建了OS层面的线程,OS层面的线程有一系列状态,此状态与Java线程的6种状态
存在映射关系(本文不展开,下一篇笔者对照JVM源码详述)。 - 因此,有的资料中试图同时表达JVM层面和OS层面的线程状态,就造成了描述不同。
结论:我们应该从JVM层面和OS层面分别理解
Java线程状态
,以及透彻理解两个层面的线程
的对应关系。
5.深入:Java线程的状态如何迁移?
JDK源码中,对Java线程的状态迁移有明确地描述。但为了深入理解,我们有必要写一些示例代码,强化理解。
PS:实战中,很多问题就源于我们并不是真的理解这些状态迁移条件。
5.1.NEW、RUNNABLE、TIME_WAITING三态迁移
- 结论:
new Thread
时,Java线程进入NEW
状态- 调用
start
方法时,Java线程进入RUNNABLE
状态 - Java线程运行
run
方法体内一旦运行Thread.sleep
方法,Java线程进入TIME_WAITING
状态 - 当
Thread.sleep
时间到,Java线程回到RUNNABLE
状态 - 其它说明:
Object.wait
、Thread.join
等方法也可以触发RUNNABLE
态迁移到TIME_WAITING
态,读者可自行实验。
- 示例代码:
- 第21行在
MyThread
构造函数中打印了线程状态、第9行在new MyThread
后也打印了线程状态,目的是从这两处打印证明new Thread
那一刻,Java线程处于NEW
状态。 - 第26行打印线程状态,为了证明第11行调用
start
方法后,Java线程进入RUNNABLE
状态。 - 第27~第31行调用
Thread.sleep
方法后,在第13~15行连续打印3次线程状态,为了证明调用Thread.sleep
方法后,Java线程进入TIME_WAITING
状态。 - 第32行打印线程状态,为了证明
Thread.sleep
方法时间到后,Java线程回到RUNNABLE
状态。
- 第21行在
- 示例代码运行结果:与预期一致。
5.2.NEW、RUNNABLE、WAITING、BLOCKED、TERMINATED五态迁移
- 结论:
new Thread
时,Java线程进入NEW
状态- 调用
start
方法时,Java线程进入RUNNABLE
状态 - Java线程运行
run
方法体内一旦运行Object.wait
方法,Java线程进入WAITING
状态 - 当另一个Java线程执行了
Object.notify
方法,Java线程进入RUNNABLE
状态 - 在Java线程从占有对象锁到失去对象锁到再次获得对象锁,即等待
synchronized
到重入synchronized
块,Java线程还会经历BLOCKED
态到RUNNABLE
态的迁移(此处也有一处伏笔,需要解读JVM源码才能清晰,下篇文章详述) - 当Java线程执行完,Java线程进入
TERMINATED
状态 - 其它说明:
Object.wait
、Thread.join
等方法五态迁移,读者可自行实验。
- 示例代码:
- 第15行为了证明
Object.wait
后,Java线程进入WAITING
态 - 第17行为了证明
Object.notify
后,Java线程进入BLOCKED
态,再重入synchronized
后进入RUNNABLE
态 - 第20行为了证明Java线程工作完成后,进入
TERMINATED
态
- 第15行为了证明
- 示例代码运行结果:与预期一致。
6.总结
- 本文通过Java官方文档和JDK源码,解读了Java线程有6种状态
- 本文又通过两段示例代码,验证了Java线程6种状态的迁移路径
- 本文遗留了一个模糊的问题:Java线程在重入
synchronized
时如何迁移到BLOCKED
态?此问题需要分析JVM源码得到进一步验证结论