Java线程状态及切换

一、什么是Java线程状态

在Java程序中,用于描述Java线程的六种状态:

  • 新建(NEW):当前线程,刚刚新建出来,尚未启动。
  • 运行(RUNNABLE):当前线程,处于竞争CPU时间分片或已经获得CPU时间片的状态。
  • 等待(WAITTING):当前线程,处于休眠,不参与CPU时间片竞争的状态。
  • 定时等待(TIMED_WAITTING):当前线程,处于定时休眠,暂时不参与CPU时间片竞争的状态。
  • 阻塞(BLOCKED):当前线程,处于阻塞,不参与CPU时间片竞争的状态。
  • 终止(TERMINATED):当前线程,处于最终停止的状态。

新建状态,只能进入运行状态。而终止状态无法再转为其他状态。

等待/定时等待与阻塞,差别就是后者需要一个事件信号(如其他线程放弃当前线程需要的排他锁),才可以进行状态切换。当然,强行关闭也是可以的。

Java线程的实现并不受JVM规范约束,故不同虚拟机的实现,往往不同。目前主流的HotSpot是将每个Java线程直接映射到一个操作系统的原生线程,从而由操作系统完成一系列的线程调度

二、哪里看Java线程状态

查看Java线程状态,主要存在三种方式:

  • java.lang.Thread.State下可以直接看到Java的六种线程状态
  • Java运行时,程序内部可以通过Thread.getState()获取目标线程状态
  • Java运行时,程序外部可以通过jstack等工具,查看线程状态

有关jstack等工具等使用,后续会有博客,专门阐述。

三、什么时候变换Java线程状态

Java线程状态的切换嘛。不啰嗦,直接上图。



这张图涵盖了Java线程状态切换的各类方法。相较网上一些图片,更为详尽一些。

如果有所遗漏,可以告诉我,我会及时填补上。

四、谁在使用Java线程状态

日常开发中,我们并不会直接与线程状态进行交互。

我们往往直接使用JDK包装好的工具,如JUC包下的各类工具等。

举个栗子

线程池中的应用

位置:com.sun.corba.se.impl.orbutil.threadpool.ThreadPoolImpl#close


  1. // Note that this method should not return until AFTER all threads have died.
  2. public void close() throws IOException {
  3. // Copy to avoid concurrent modification problems.
  4. List<WorkerThread> copy = null;
  5. synchronized (workersLock) {
  6. copy = new ArrayList<>(workers);
  7. }
  8. for (WorkerThread wt : copy) {
  9. wt.close();
  10. while (wt.getState() != Thread.State.TERMINATED) {
  11. try {
  12. wt.join();
  13. } catch (InterruptedException exc) {
  14. wrapper.interruptedJoinCallWhileClosingThreadPool(exc, wt, this);
  15. }
  16. }
  17. }
  18. threadGroup = null;
  19. }

实际查看JDK后发现,JDK中其实也没有辣么多的实例,并且大多数直接关联线程状态的,也是一些状态查看的东东。

这在文章开头就说,Java线程操作,是很底层的,甚至其实现都不包含在虚拟机规范中。

主流的HotSpot,也是直接将Java线程映射到系统线程,由系统进行一系列的线程调度处理。

所以,在JDK中,是直接对线程状态进行处理的部分是很少的。

五、为什么需要线程状态

1.为什么需要线程状态这一概念

这个问题,可以从两个角度来说明:生命周期与资源管理

  • 一方面,线程状态很好地刻画了线程的整个生命周期,对生命周期中不同阶段进行了有效划分。
  • 另一方面,资源是有限的,需求是无限的。所以需要将系统资源有意识地进行调度,合理利用比较优势,追求帕累托最优。

实现后者的,就是利用线程在生命周期的不同阶段这一天然属性带来的状态刻画,进行的分组。CPU调度只需要关注运行状态的线程。而阻塞,等待等线程,都有着属于自己的一套处理方式。最终获得资源(开发时对复杂性的应对,运行时对系统资源对消耗,应用者心智模型的成长等)的优化分配。

2.JDK中为什么需要定义Java线程状态

前文有说到,Java中很少直接使用到线程状态。那么为什么还要在JDK中定义Java的六种线程状态呢?

一方面,通过信息透明的方式,降低使用者使用时建立心智模型的成本。如,现在的我们可以通过打印日志,打断点,快速了解Java的各个线程状态,并清楚了解产生的原因。从而大大提高了我们对Java线程的认识,进而更为愉快地拥抱JUC包下诸如线程池等工具。

另一方面,通过可以直接应用的状态枚举,我们可以很好地对现有工具进行二次开发等。如我们可以通过扩展AQS,并在其中添加线程状态的校验,从而得到定制化的线程同步工具。

六、如何使用线程状态

使用的方式,我已经在上文说了:学习Java线程,定制线程相关工具开发。

这里给出一个有关线程学习的demo:


  1. /**
  2. * @program: learning
  3. * @description: 用于确认线程状态问题
  4. * @author: Jarry
  5. * @create: 2020-10-26 22:25
  6. **/
  7. public class ThreadState {
  8. public static void main(String[] args) {
  9. threadStateTest();
  10. // threadStateTest2();
  11. // threadStateWithBlocked();
  12. // threadStateWithException();
  13. // threadStateWithSuspend();
  14. }
  15. /**
  16. * 实践证明:Thread.suspend()与Thread.resume()不会改变线程状态
  17. * 线程状态该是Waiting,就Waiting。该Timed_Waiting就Timed_Waiting
  18. * Thread.suspend()与Thread.resume()只是挂起目标线程(并且不会释放锁资源)
  19. */
  20. private static void threadStateWithSuspend() {
  21. Thread thread1 = new Thread(() -> {
  22. // LockSupport.park();
  23. LockSupport.parkNanos(2000000000);
  24. });
  25. thread1.start();
  26. printThreadState(thread1);
  27. LockSupport.parkNanos(500000000);
  28. printThreadState(thread1);
  29. thread1.suspend();
  30. printThreadState(thread1);
  31. LockSupport.parkNanos(500000000);
  32. printThreadState(thread1);
  33. thread1.resume();
  34. LockSupport.parkNanos(500000000);
  35. printThreadState(thread1);
  36. // LockSupport.unpark(thread1);
  37. }
  38. /**
  39. * 展现线程阻塞状态
  40. */
  41. private static void threadStateWithBlocked() {
  42. Runnable runnable = new Runnable() {
  43. @Override
  44. public void run() {
  45. synchronized (ThreadState.class) {
  46. // LockSupport.parkNanos(2000000000);
  47. LockSupport.park();
  48. }
  49. }
  50. };
  51. Thread thread1 = new Thread(runnable);
  52. Thread thread2 = new Thread(runnable);
  53. thread1.start();
  54. LockSupport.parkNanos(500000000);
  55. thread2.start();
  56. // 加上以下时间间隔,则结果:Runnable->Blocked
  57. // 推论:Thread.start()会将线程状态设置为Runnable,然后在遇到sync的锁,再切换为Blocked状态
  58. // LockSupport.parkNanos(500000000);
  59. printThreadState(thread2);
  60. LockSupport.parkNanos(500000000);
  61. printThreadState(thread2);
  62. LockSupport.parkNanos(500000000);
  63. LockSupport.unpark(thread1);
  64. LockSupport.unpark(thread2);
  65. }
  66. /**
  67. * 由于底层实现机制的不同(相较于其他waiting的方法),无法直接进行Object.wait(),否则会抛出以下异常
  68. * @exception java.lang.IllegalMonitorStateException
  69. * object.wait()进行wait的方法,是直接对其wait_set进行操作
  70. */
  71. private static void threadStateWithException() {
  72. Thread thread1 = new Thread(() -> {
  73. try {
  74. ThreadState.class.wait(2000);
  75. } catch (InterruptedException e) {
  76. e.printStackTrace();
  77. }
  78. });
  79. thread1.start();
  80. LockSupport.parkNanos(1000000000);
  81. printThreadState(thread1);
  82. }
  83. /**
  84. * Object.wait()的使用
  85. */
  86. private static void threadStateTest3() {
  87. Thread thread1 = new Thread(() -> {
  88. synchronized (ThreadState.class) {
  89. try {
  90. ThreadState.class.wait(2000);
  91. } catch (InterruptedException e) {
  92. e.printStackTrace();
  93. }
  94. }
  95. });
  96. thread1.start();
  97. LockSupport.parkNanos(1000000000);
  98. printThreadState(thread1);
  99. }
  100. /**
  101. * 确定LockSupport.parkNacos()是否可以生成Time_Waiting状态
  102. */
  103. private static void threadStateTest2() {
  104. Thread thread1 = new Thread(() -> {
  105. LockSupport.parkNanos(2000000000);
  106. });
  107. thread1.start();
  108. printThreadState(thread1);
  109. LockSupport.parkNanos(1000000000);
  110. printThreadState(thread1);
  111. }
  112. /**
  113. * 查看到除Blocked以外的所有线程状态
  114. */
  115. private static void threadStateTest() {
  116. Thread thread1 = new Thread(() -> {
  117. synchronized (ThreadState.class) {
  118. // Runnable
  119. printThreadState(Thread.currentThread());
  120. // 1.Thread.sleep(time)
  121. try {
  122. Thread.sleep(2000);
  123. } catch (InterruptedException e) {
  124. e.printStackTrace();
  125. }
  126. // 2.Object.wait(time)
  127. // try {
  128. // ThreadState.class.wait(2000);
  129. // } catch (InterruptedException e) {
  130. // e.printStackTrace();
  131. // }
  132. // 3.Thread.join(time)
  133. // try {
  134. // Thread.currentThread().join(2000);
  135. // } catch (InterruptedException e) {
  136. // e.printStackTrace();
  137. // }
  138. // 4.LockSupport.parkNanos(time);
  139. // LockSupport.parkNanos(2000000000);
  140. // 5.LockSupport.parkUntil(timeStamp);
  141. // LockSupport.parkUntil(System.currentTimeMillis()+2000);
  142. LockSupport.park();
  143. }
  144. });
  145. thread1.setName("test_thread");
  146. // New
  147. printThreadState(thread1);
  148. thread1.start();
  149. LockSupport.parkNanos(1000000000);
  150. // Timed_waiting
  151. printThreadState(thread1);
  152. LockSupport.parkNanos(2000000000);
  153. // Waiting
  154. printThreadState(thread1);
  155. LockSupport.unpark(thread1);
  156. LockSupport.parkNanos(1000000000);
  157. // Terminated
  158. printThreadState(thread1);
  159. }
  160. private static void printThreadState(Thread thread) {
  161. System.out.println("current Thread(" + thread.getName()+":" + thread.getId() + ") state:" + thread.getState());
  162. }
  163. }

代码中有一些细碎的知识点,就不在这里赘述了。感兴趣的小伙伴,可以自己看看注释,自行验证。

七、扩展:系统状态(三态&五态)

操作系统就包含进程管理,作业管理,文件管理等。其中进程管理,就涉及系统状态的三态模型与五态模型。

其中,三态模型包含以下三种状态:

  • 就绪状态
  • 运行状态
  • 阻塞状态

而五态模型则包含以下五种状态:

  • 运行状态
  • 静止就绪态
  • 活跃就绪态
  • 静止阻塞态
  • 活跃阻塞态

具体状态切换等,可以看我之前写的一篇博客:

系统架构设计师-操作系统

最后,愿与诸君共进步。

八、附录

补充:WAITTING/TIMED_WAITTING与BLOCKED的区别

其实,我之前也挺纠结这个问题的。

当时的想法是WAITTING/TIMED_WAITTING是由JVM自己维持,而BLOCKED是由系统维持的。后面看到主流的HotSpot是将线程映射到系统原生线程的,所以这个想法大概率是错误的。

那么两者区别是什么呢?

直到我在上文的示例代码中,BLOCKED状态的线程,在其他线程释放其所需的锁时,该线程是先转为RUNNING状态,再变为其他状态。这引起我的注意。

最后在stackOverFlow的一篇文章(Difference between WAIT and BLOCKED thread states)中,看到这样的解释:

The important difference between the blocked and wait states is the impact on the scheduler. A thread in a blocked state is contending for a lock; that thread still counts as something the scheduler needs to service, possibly getting factored into the scheduler's decisions about how much time to give running threads (so that it can give the threads blocking on the lock a chance).

Once a thread is in the wait state the stress it puts on the system is minimized, and the scheduler doesn't have to worry about it. It goes dormant until it receives a notification. Except for the fact that it keeps an OS thread occupied it is entirely out of play.

This is why using notifyAll is less than ideal, it causes a bunch of threads that were previously happily dormant putting no load on the system to get woken up, where most of them will block until they can acquire the lock, find the condition they are waiting for is not true, and go back to waiting. It would be preferable to notify only those threads that have a chance of making progress.

(Using ReentrantLock instead of intrinsic locks allows you to have multiple conditions for one lock, so that you can make sure the notified thread is one that's waiting on a particular condition, avoiding the lost-notification bug in the case of a thread getting notified for something it can't act on.)

简单说,就是CPU时间片不会考虑WAITTING/TIMED_WAITTING状态。

但是,虽然BLOCKED状态的线程无法获得CPU时间片,但是系统调度时,依旧会考虑BLOCKED状态的线程,将其置于调度计算中。

如果哪位小伙伴对这方面有了解,希望可以聊一聊。

参考

Java线程状态及切换的更多相关文章

  1. Java线程状态切换以及核心方法

    1.Java线程状态 1.1 线程主要状态 ①初始(NEW):新创建了一个线程对象,但还没有调用start()方法.②运行(RUNNABLE):Java线程中将就绪(ready)和运行中(runnin ...

  2. Java线程状态和关闭线程的正确姿势

    1.线程状态及切换 Java中的线程有六种状态,使用线程Thread内的枚举类来实现,如下,我对每个状态都进行了一定的解释. public enum State { /** 表示一个线程还没启用(即未 ...

  3. 浅谈 Java线程状态转换及控制

    线程的状态(系统层面) 一个线程被创建后就进入了线程的生命周期.在线程的生命周期中,共包括新建(New).就绪(Runnable).运行(Running).阻塞(Blocked)和死亡(Dead)这五 ...

  4. Java线程状态Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

    一.线程5种状态 新建状态(New) 新创建了一个线程对象. 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中,变得可运行,等待获 ...

  5. Java线程状态转换

    前言:对于Java线程状态方面的知识点,笔者总感觉朦朦胧胧,趁着最近整理资料,将Java线程状态方面的知识点总结归纳,以便加深记忆. 1.Java线程状态值 在Thread类源码中通过枚举为线程定义了 ...

  6. Java线程状态及 wait、sleep、join、interrupt、yield等的区别

    Java中的线程状态(详见Java线程状态及转换-MarchOn): wait:Object类的实例方法,释放CPU执行权,进入等待状态,直到  被中断.被拥有该对象锁的线程唤醒(notify或not ...

  7. 从源码看java线程状态

    关于java线程状态,网上查资料很混乱,有的说5种状态,有的说6种状态,初学者搞不清楚这个线程状态到底是怎么样的,今天我讲一下如何看源码去解决这个疑惑. 直接上代码: public class Thr ...

  8. 面试官:都说阻塞 I/O 模型将会使线程休眠,为什么 Java 线程状态却是 RUNNABLE?

    摘要: 原创出处 https://studyidea.cn 「公众号:程序通事 」欢迎关注和转载,保留摘要,谢谢! 使用 Java 阻塞 I/O 模型读取数据,将会导致线程阻塞,线程将会进入休眠,从而 ...

  9. Java线程状态、线程start方法源码、多线程、Java线程池、如何停止一个线程

    下面将依次介绍: 1. 线程状态.Java线程状态和线程池状态 2. start方法源码 3. 什么是线程池? 4. 线程池的工作原理和使用线程池的好处 5. ThreadPoolExecutor中的 ...

随机推荐

  1. 深入浅出具有划时代意义的G1垃圾回收器

    G1诞生的背景 Garbage First(简称G1)收集器是垃圾收集器技术发展历史上的里程碑式的成果,它开创了收集器面向局部收集的设计思路和基于Region的内存布局形式.HotSpot开发团队最初 ...

  2. PJzhang:鸟哥的linux私房菜-shell脚本-上

    猫宁~~~ 建议全程在centos7中进行,我在kali linux中有些命令无法执行. 1~家目录下创建bin文件,test.sh文件在bin目录 下面的shell代码打印Hello World! ...

  3. 应用启动失败,报错:The server experienced an unexpected error when processing the request

    前言 在腾讯云TKE集群中部署服务的时候,预警服务,warn一直重启,经过查询日志发现了如下的错误 The server experienced an unexpected error when pr ...

  4. vue 异步提交php 两种方式传值

    1.首先要在php的入口文件写上一条代码,允许异步提交 header("ACCESS-CONTROL-ALLOW-ORIGIN:*"); 2.在vue有两种方式将数据异步提交到ph ...

  5. java 的 callback

    Java 本身没有回调这一说,但是面向对象可以模拟出来. 1. 回调接口对象 ICommand package com.git.Cmder; public interface ICommand { v ...

  6. 用 C 语言游戏编程开发!果然最担心的事又发生了!

    30了.我要怎么办,老了.人就像一头小毛驴,方向都是牵着的人定的. 这个项目从去年开始的,一个手机游戏,当时接这个项目的时候其实没有太多考虑,我一向都喜欢打肿脸充胖子的,好面子,人家找上门来,不能不给 ...

  7. centos8平台给sudo配置日志

    一,sudo日志的用途: 我们可以记录下来用户账号在哪个时间进行过sudo 这样不需要再从secure日志中查找用户的sudo记录 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://w ...

  8. Python操作CSV和Excel

    概述 csv是最通用的文件格式,本质是文本文件,用记事本即可打开.同一行中每个字段间用逗号分隔,在csv中显示的是在不同单元格中,在记事本中显示的是一行中用逗号分隔. xls是excel专用格式,是二 ...

  9. C++ 设置软件激活不息屏

    SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);

  10. 第二个 SignalR,可以私聊的聊天室

    一.简介 上一次,我们写了个简单的聊天室,接下来,我们来整一个可以私聊的聊天室. SignalR 官方 API 文档 需求简单分析: 1.私聊功能,那么要记录用户名或用户ID,用于发送消息. 2.怎么 ...