image-20200907162110788.png

线程状态

线程状态值可以通过Thread.getState()调用获取得到一个 java.lang.Thread.State 的枚举类型(Enum),定义了如下几种情况(均为大写):

  1. New:线程已创建而未启动,一个实例线程只能被启动一次,即一个线程只能一次处于该状态;
  2. Runnable:可以看成复合状态,包括两个子状态:
    • Ready:表示该线程可以被线程调度器进行调度而使之处于Running状态(又叫活跃线程);
    • Running:表示该线程run方法正在执行,执行 Thread.yield() 的线程,相应状态可能会由Running转为Ready。
  3. Blocked:一个线程发起一个阻塞式 I/O(Blocking I/O) 操作后,或者申请一个由其它线程持有的独占资源(比如锁)时,相应的线程会处于该状态。处于Blocked状态的线程并不占用处理器资源,当阻塞式I/O操作完成后或者线程获得了其申请的资源,该线程的状态又可以转换为Runnable。
  4. Waiting:执行了特定方法就会处于这种等待其他线程执行另外一些特定操作的状态:
    • Object.wait()
    • Thread.join()
    • LockSupport.park(Object)
    • 由waiting转为Runnable状态的方法:
      • Object.notify()/notifyAll()
      • LockSupport.unpack(Object)
  5. Time_waiting:类似Waiting,差别在于该状态的线程并非无限制的等待其他线程执行特定操作,而是处于带时间限制的等待状态。当其他线程没有在指定时间内执行该线程所期望的特定操作时,该线程状态自动转为Runnable。下列为带超时的方式:
    • Thread.sleep()
    • Object.wait()
    • Thread.join()
    • LockSupport.parkNanos()
    • LockSupport.parkUntil()
  6. Terminated:已经执行结束的线程处于该状态。一个线程只能处于一次该状态,Thread.run()正常返回或抛出异常提前终止都会处于该状态。

线程状态流转

save_share_review_picture_1599578802.jpeg

  1. 初始线程处于 NEW 状态,调用 start() 之后开始执行,进入 RUNNING 或者 READY 状态。如果没有调用任何的阻塞函数,线程只会在 RUNNING 和 READY 之间切换,也就是系统的时间片调度。这两种状态的切换是操作系统完成的,开发者基本没有机会介入,除了可以调用 yield() 函数,放弃对 CPU 的占用。
  2. 一旦调用了图中的任何阻塞函数,线程就会进入 WAITING 或者 TIMED_WAITING 状态,两者的区别只是前者为无限期阻塞,后者则传入了一个时间参数,阻塞一个有限的时间。如果使用了 synchronized 关键字或者 synchronized 块,则会进入 BLOCKED 状态。
  3. 除了常用的阻塞/唤醒函数,还有一对不太常见的阻塞/唤醒函数,LockSupport.park()/unpark()。这对函数非常关键,Concurrent 包中 Lock 的实现即依赖这一对操作原语。故而 t.interrupted() 的精确含义是“唤醒轻量级阻塞”,而不是字面意思“中断一个线程”。

轻量级阻塞和重量级阻塞:

  • 能够被中断的阻塞称为轻量级阻塞,对应的线程状态是WAITING或者TIMED_WAITING
  • synchronized 这种不能被中断的阻塞称为重量级阻塞,对应的状态是BLOCKED
  1. // pom.xml配置
  2. <dependency>
  3. <!-- 单元测试 -->
  4. <groupId>junit</groupId>
  5. <artifactId>junit</artifactId>
  6. <version>4.13</version>
  7. <scope>compiler</scope>
  8. </dependency>
  9. // 测试
  10. public class StateTest {
  11. /**
  12. * 输出当前的线程信息
  13. *
  14. * @param thread
  15. */
  16. private static void print(Thread thread, String state) {
  17. final StringBuilder builder = new StringBuilder();
  18. builder.append(state)
  19. .append("\n\tThread Name: ")
  20. .append(thread.getName())
  21. .append("\n\t")
  22. .append("Thread State: ")
  23. .append(thread.getState())
  24. .append("\n");
  25. System.out.println(builder.toString());
  26. }
  27. @Test
  28. public void testThreadState() throws InterruptedException {
  29. // 状态切换:新建->运行->终止
  30. Runnable runnable = () -> {
  31. print(Thread.currentThread(), "运行时:");
  32. };
  33. final Thread thread = new Thread(runnable, "测试线程");
  34. // 输出刚创建Thread,没调用start方法的状态
  35. print(thread, "未调用start方法:");
  36. // 线程运行时的状态
  37. thread.start();
  38. // 线程结束后的状态
  39. // 主线程休眠2s,确保测试线程结束
  40. TimeUnit.SECONDS.sleep(2);
  41. print(thread, "线程结束后:");
  42. }
  43. @Test
  44. public void testThreadTimeSleepState() throws InterruptedException {
  45. // 状态切换:新建->运行->等待->运行->终止
  46. Runnable runnable = () -> {
  47. print(Thread.currentThread(), "休眠之前:");
  48. try {
  49. // 当前线程休眠2s
  50. TimeUnit.SECONDS.sleep(2);
  51. } catch (InterruptedException e) {
  52. e.printStackTrace();
  53. }
  54. print(Thread.currentThread(), "休眠之后:");
  55. };
  56. final Thread thread = new Thread(runnable, "测试线程");
  57. // 输出刚创建Thread,没调用start方法的状态
  58. print(thread, "未调用start方法:");
  59. // 线程运行时的状态
  60. thread.start();
  61. // 线程休眠状态
  62. // 主线程休眠1s,确保测试线程进入休眠
  63. TimeUnit.SECONDS.sleep(1);
  64. print(thread, "主线程休眠1s的状态:");
  65. // 主线程休眠4s,确保测试线程结束
  66. TimeUnit.SECONDS.sleep(4);
  67. print(thread, "主线程休眠4s后的线程状态:");
  68. }
  69. @Test
  70. public void testThreadBlockingState() throws InterruptedException {
  71. // 线程状态:新建->运行->阻塞->运行—>终止
  72. Runnable runnable = () -> {
  73. // 对当前对象加锁
  74. synchronized (this) {
  75. print(Thread.currentThread(), "获得锁:");
  76. }
  77. };
  78. final Thread thread = new Thread(runnable, "测试线程");
  79. // 对当前对象加锁,这样thread将无法获取锁
  80. synchronized (this) {
  81. print(thread, "测试线程未启动的状态:");
  82. // 启动测试线程,会被阻塞
  83. thread.start();
  84. // 获取thread线程状态
  85. print(thread, "测试线程刚启动状态:");
  86. // 等待2s,再看状态
  87. TimeUnit.SECONDS.sleep(2);
  88. print(thread, "测试线程启动后2s:");
  89. }
  90. // 主线程等待2s,等待测试线程获得锁后执行
  91. TimeUnit.SECONDS.sleep(2);
  92. print(thread,"测试线程最终状态:");
  93. }
  94. }