1. 不那么重要(主要帮助理解线程问题),面试很少遇到

6种线程状态

1. NEW :

线程刚刚创建,还没有启动

2. RUNNABLE :

可运行状态,由线程调度器可以安排执行(线程被调度器选中执行)
底下又分两种小状态:ready、running
yield方法:从running->ready

3. WAITING: .

等待被唤醒

4. TIMED WAITING:

隔一段时间后自动唤醒
sleep

5. BLOCKED:

被阻塞,正在等待锁.
synchronized上到某个锁上,这个时候还没有拿到锁,需要等待锁—->此时被阻塞

6. TERMINATED:

线程结束

线程状态之间的状态迁移

图解

image.png

解释

  1. 线程被new出来了但没有start时,处于NEW状态
  2. 线程执行结束处理TERMINATED
  3. cpu分配时间片给线程执行(分时技术===>时间片、时间片轮转、轮询?)
    1. 获得时间片的线程处于RUNNING
    2. 没有获得cpu的处于READY,暂时让出了cpu,等待再次被调度去执行(使用yield方法到达READY)
  4. 等待进入同步代码的锁synhronized,正在等待某一把锁但是还没有竞争到这把锁,正在抢这把锁的线程处于BLOCKED(阻塞)
  5. 并没有进入阻塞状态,没有进入阻塞队列,等着被唤醒后才可以执行,而是说进入了一个忙等待的状态(即自旋的等待)的线程处于WAITING状态===>o.wait(); t.join(); LockSupport.park(); Lock.lock();
  6. 注:等锁的过程除了synchronized这种状态叫做BLOCKED之外,其他都叫WAITING(可以不深究,没什么人会问)
  7. 隔一段时间之后自动醒过来的线程处于TIMED WAITING状态===>Thread.sleep(time); o.wait(time); t.join(time); LockSupport.parkNanos(); LockSupport.parkUntil();(凡是带时间的就处在TIMED WAITING状态)
  8. 上图中的线程状态全是由jvm管理的,因为jvm管理这些状态时也要通过操作系统(哪个是jvm,哪个是os,这两个分不开,jvm是跑在os上的普通程序)
  9. 杀死线程也算terminated
  10. 线程挂起就是说线程从cpu时间片上移除,进入等待状态?ready?
  11. 上图中的线程状态与os中的是一一对应的吗?这个要看jvm的实现,以前是一一对应的,现在不好说,但是纤程必然不是一一对应的;正常是一一对应的
  12. java中ready和running两种状态合并为runnable状态,所以处于runnable状态时可能在就绪队列中等待,也可能正在运行
  13. os中是进程的概念,linux中的线程是轻量级进程,与进程状态差不多
  14. 操作系统中挂起的概念是暂时放到外存中去!!!
  15. 调用哪些方法,线程会释放锁;调用那些方法,线程不会释放锁?
    1. 不释放锁:
      1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep(Long l)、Thread.yield()方法暂停当前线程的执行
      2. 线程执行同步代码块时,其它线程调用该线程suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)===>t.suspend一般由其他线程调用,而不是自己调用
      3. 尽量避免使用suspend()和resume()来控制线程
    2. 释放锁:
      1. 当前线程的同步方法、同步代码块执行结束
      2. 当前线程的同步方法、同步代码块遇到break、return终止该代码块、该方法的继续执行
      3. 当前线程的同步方法、同步代码块中出现了未处理Error和Exception,导致异常结束
      4. 当前线程在同步方法、同步代码块中执行了线程对象的wait()方法,当前线程暂停,并释放锁

SleepHelper帮助类(只是简答地将try……catch……扔到帮助类中)

在演示式的代码中写很多try……catch……,看起来费劲

  1. package com.mashibing.util;
  2. import java.util.concurrent.TimeUnit;
  3. public class SleepHelper {
  4. public static void sleepSeconds(int seconds) {
  5. try {
  6. TimeUnit.SECONDS.sleep(seconds) ;
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. }
  11. }

用代码说明状态转化

  1. package com.mashibing.juc.c_000_threadbasic;
  2. import com.mashibing.util.SleepHelper;
  3. import java.util.concurrent.TimeUnit;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.LockSupport;
  6. import java.util.concurrent.locks.ReentrantLock;
  7. /**
  8. * title:${file_name}
  9. * 关于线程状态的实验
  10. *
  11. * @author 马士兵 http://www.mashibing.com
  12. * @version 2.0
  13. * @date ${date}
  14. */
  15. public class T04_ThreadState {
  16. public static void main(String[] args) throws Exception {
  17. //1===================================================
  18. Thread t1 = new Thread(() -> {
  19. System.out.println("2: " + Thread.currentThread().getState());
  20. for (int i = 0; i < 3; i++) {
  21. SleepHelper.sleepSeconds(1);
  22. System.out.print(i + " ");
  23. }
  24. System.out.println();
  25. });
  26. System.out.println("1: " + t1.getState());
  27. t1.start();
  28. // 等待t1线程结束
  29. // 在主方法中调用join,就说明主线程要等待t1线程执行结束才能继续向下执行
  30. t1.join();
  31. System.out.println("3: " + t1.getState());
  32. //2===================================================
  33. Thread t2 = new Thread(() -> {
  34. try {
  35. LockSupport.park();
  36. System.out.println("t2 go on!");
  37. TimeUnit.SECONDS.sleep(5);
  38. } catch (InterruptedException e) {
  39. e.printStackTrace();
  40. }
  41. });
  42. t2.start();
  43. // 睡1秒钟,确保t2开始执行并被阻塞park
  44. // 为什么这里不要try……catch……???
  45. TimeUnit.SECONDS.sleep(1);
  46. System.out.println("4: " + t2.getState());
  47. LockSupport.unpark(t2);
  48. // 睡1秒钟,确保t2被唤醒
  49. // 为什么这里不要try……catch……???
  50. TimeUnit.SECONDS.sleep(1);
  51. // 为什么会TIMED_WAITING,因为t2醒了之后会sleep
  52. System.out.println("5: " + t2.getState());
  53. //3===================================================
  54. final Object o = new Object();
  55. Thread t3 = new Thread(() -> {
  56. synchronized (o) {
  57. System.out.println("t3 得到了锁 o");
  58. }
  59. });
  60. new Thread(() -> {
  61. // 线程申请这把锁,却拿不着的时候处于BLOCKED状态!!!
  62. synchronized (o) {
  63. SleepHelper.sleepSeconds(5);
  64. }
  65. }).start();
  66. SleepHelper.sleepSeconds(1);
  67. t3.start();
  68. SleepHelper.sleepSeconds(1);
  69. System.out.println("6: " + t3.getState());
  70. //4===================================================
  71. final Lock lock = new ReentrantLock();
  72. Thread t4 = new Thread(() -> {
  73. lock.lock(); //省略try finally,为了看得清楚,重点看业务逻辑
  74. // ===>JUC中的锁===>CAS来实现(忙等待)===>不会进入BLOCKED状态,而是进入WAITING状态
  75. // 只有synchronized这样的锁才进入BLOCKED状态
  76. System.out.println("t4 得到了锁 o");
  77. lock.unlock();
  78. });
  79. new Thread(() -> {
  80. lock.lock();
  81. SleepHelper.sleepSeconds(5);
  82. lock.unlock();
  83. }).start();
  84. // 保证上锁完成
  85. SleepHelper.sleepSeconds(1);
  86. t4.start();
  87. SleepHelper.sleepSeconds(1);
  88. System.out.println("7: " + t4.getState());
  89. //5===================================================
  90. Thread t5 = new Thread(() -> {
  91. LockSupport.park();
  92. });
  93. t5.start();
  94. SleepHelper.sleepSeconds(1);
  95. // 此时t5处于WAITING状态
  96. System.out.println("8: " + t5.getState());
  97. LockSupport.unpark(t5);
  98. }
  99. }

输出结果

  1. 1: NEW
  2. 2: RUNNABLE
  3. 0 1 2
  4. 3: TERMINATED
  5. 4: WAITING
  6. t2 go on!
  7. 5: TIMED_WAITING
  8. 6: BLOCKED
  9. 7: WAITING
  10. t3 得到了锁 o
  11. 8: WAITING
  12. t4 得到了锁 o

线程状态在Lock和synchronized的区别

  1. 等待synchronized锁时,线程处在BLOCKED状态,此时线程被放进了等待队列中,等待调度器调度执行
  2. 等待Lock锁时,线程处在WAITING状态===>JUC中的锁===>CAS来实现(忙等待)===>不会进入BLOCKED状态,而是进入WAITING状态
  3. 避免Lock.lock()和synchronized混在一起
  4. 只有synchronized会处于BLOCKED状态,而其他状态处于WAITING或者TIMED_WAITING状态

总结

  1. 6种状态分别在什么情况下出现总结
  2. BLOCKED算是一个比较严重的阻塞状态===>只有在参与争夺synchronized锁时会出现这种状态,其他的都没有
  3. synchronized是要经过操作系统去调度的。可以这么说,只有要经过操作系统调度的,才会有这个BLOCKED状态,其他情况下都没有===>去理发店理发,理发店比较忙,店长(操作系统)出来把你安排到旁边休息区(小屋)去等待休息,什么时候轮到你了会把你叫出来;而WAITING或者TIMED_WAITING是持有这把锁了、自己阻塞了、忙等待、不进小屋、就在理发的人旁边看着(等?),因为这个理发师特别好,所以就在旁边看着,等着去抢;店长说马上到你了,你先睡5秒,这就是TIMED_WAITING;理发师是cpu,他说能服务两个人,一个人在理发,一个人坐在理发椅上就行,理发师是一会服务你,一会服务他(正在服务的叫RUNNING,坐在椅子上的为READY)
  4. yield方法调用之后会让出cpu,自己进入等待队列,这时可能会把cpu让给等待队列中的另一线程,也有可能还是分配给原来的线程
  5. synchronized获取了哪把锁,就是用哪把锁的wait和notify

多线程不怎么能用到,写中间件的机会很少,写底层的机会也很少;所以重点应该放在面试问的多的地方——>面向面试学习

轻松简单!