1.疑惑:Java的线程究竟有几种状态?

在Java的并发编程技术中,经常会看到Java线程的状态迁移图。

  • 有的这样画

image-20230330130053911

  • 有的这样画

image-20230330130208531

  • 有的这样画:

image-20230330130240613

笔者刚接触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

image-20230330130905543

  • 在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.javaState枚举类:

image-20230331111439960

  • NEW:当Java线程处于此状态时,创建线程那一刻,线程的状态。

image-20230405132055300

  • RUNNABLE:当Java线程处于此状态时,JVM有可能去执行它(此处埋有伏笔)。

image-20230405132344766

  • BLOCKED:此状态,与synchronizedObject.wait强相关(后文代码演示)。

image-20230405133442702

  • WAITING:此状态与BLOCKED一样,与Object.waitThread.join等相关(后文代码演示)。

image-20230405133702393

  • TIMED_WAITING:与WAITING类似的一种状态,与Thread.sleepObject.waitThread.join等有关(后文代码演示)。

image-20230405134328837

  • TERMINATED:终结态,没啥好说的。

image-20230405134725161

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线程状态,以及透彻理解两个层面的线程的对应关系

image-20230405140832691

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.waitThread.join等方法也可以触发RUNNABLE态迁移到TIME_WAITING态,读者可自行实验。

image-20230405142942728

  • 示例代码
    • 第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状态。

image-20230405144125704

  • 示例代码运行结果:与预期一致。

image-20230405144303047

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.waitThread.join等方法五态迁移,读者可自行实验。

image-20230405151245195

  • 示例代码
    • 第15行为了证明Object.wait后,Java线程进入WAITING
    • 第17行为了证明Object.notify后,Java线程进入BLOCKED态,再重入synchronized后进入RUNNABLE
    • 第20行为了证明Java线程工作完成后,进入TERMINATED

image-20230405152223300

  • 示例代码运行结果:与预期一致。

image-20230405152517526

6.总结

  • 本文通过Java官方文档和JDK源码,解读了Java线程有6种状态
  • 本文又通过两段示例代码,验证了Java线程6种状态的迁移路径
  • 本文遗留了一个模糊的问题:Java线程在重入synchronized时如何迁移到BLOCKED态?此问题需要分析JVM源码得到进一步验证结论