首页 线程是如何在6种状态之间转换的
文章
取消

线程是如何在6种状态之间转换的

线程的6种状态

  1. New(新建)
  2. Runnable(可运行)
  3. Blocked(阻塞)
  4. Waiting(等待)
  5. Timed Waiting(计时等待)
  6. Terminated(终止)

查看当前线程的状态:Thread.currentThread().getState()

线程状态转换图

New(新建)

New表示线程新创建但还未启动的状态,即:通过new Thread()创建,但还未调用start()方法。

一旦调用start(),就从New态转换为Runnable态

Runnable(可运行)

Java的Runnable状态对应操作系统线程状态的两种状态,分别是:Running和Ready,即Runnable状态的线程可能正在运行,也可能正在等待被分配CPU资源

因此当执行线程A的CPU被调度去执行其他任务,线程A依然是Runnable状态,因为CPU随时有可能被调度回来继续执行任务

阻塞状态

Java中阻塞状态通常不仅仅是 Blocked,实际上它包括三种状态,分别是 Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待),这三 种状态统称为阻塞状态

Blocked(阻塞)

什么时候进入Blocked状态

从Runnable进入Blocked状态,就是在线程A进入synchronized代码块或synchronized方法的时候没有抢到monitor锁,如果线程B释放锁,并且线程A抢到monitor锁,就会从Blocked状态回到Runnable状态

Waiting(等待)

什么时候进入Waiting状态

从Runnable进入Waiting状态,由三种可能:

  1. 没有设置 Timeout 参数的 Object.wait() 方法。
  2. 没有设置 Timeout 参数的 Thread.join() 方法。
  3. LockSupport.park() 方法。

第三种可能解释

Blocked状态仅仅针对synchronized monitor锁,但是在Java种还有很多其他锁,比如ReentrantLock,如果线程在获取这种锁时没有抢到该锁就会进入 Waiting 状态,因为本质上它执行了 LockSupport.park() 方法,所以会进入 Waiting 状态。

Blocked于Waiting的区别

Blocked 在等待其他线程释放 monitor 锁,而 Waiting 则是在等待某个条件,比如 join 的线程执行完毕,或者是 notify()/notifyAll() 。

Timed Waiting(计时等待)

Waiting(等待)和Timed Waiting(计时等待)两种状态非常相似,区别仅在于有没有时间限制,Timed Waiting 会等待超时,由系统自动唤醒,或者在超时前被唤醒信号唤醒。

  1. 设置了时间参数的 Thread.sleep(long millis) 方法;
  2. 设置了时间参数的 Object.wait(long timeout) 方法;
  3. 设置了时间参数的 Thread.join(long millis) 方法;
  4. 设置了时间参数的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法。

如何从阻塞状态(Block、Waiting、Timed Waiting)转换成其他状态

Blocked(阻塞)转换成Runnable

Blocked的线程如果获得了monitor锁,就会 ==> Runnable(可运行)

Waiting(等待)如何转换成其他状态

Waiting比较特殊,因为它是不限时等待,所以它不会自己主动恢复到Runnable状态,因此需要被通知,或者其他线程中断/结束

比如:当执行了LockSupport.unpark(线程A),或者线程A.join()的线程运行结束/中断时才可以进入Runnable状态

Timed Waiting(计时等待)如何转换成其他状态

如果超时时间到了,且能够直接获取到锁(比如ReentrantLock)或者join的线程运行结束/被中断/调用了LockSupport.unpark(线程t),就会直接恢复成Runnable状态

如果其他线程调用notify()或notifyAll()来唤醒线程A会发生什么

首先唤醒线程A的线程B必须持有monitor锁,所以处于Waiting状态的线程被唤醒时拿不到monitor锁,所以会进入Blocked状态,只有线程B执行完毕并且释放monitor锁,才可能轮到线程A去抢夺monitor锁,如果抢到就会转换到Runnable状态,对于 Timed Waiting(计时等待)也是一样的

Terminated(终止)

如何进入 Terminated(终止)状态

  1. run() 方法执行完毕,线程正常退出。
  2. 出现一个没有捕获的异常,终止了 run() 方法,最终导致意外终止。

线程应该注意的点

线程状态转换图

如上图所示:

  1. 线程的状态是需要按照箭头方向来走的,比如线程从 New 状态是不可以直接进入 Blocked 状态的,它需要先经历 Runnable 状态。
  2. 线程生命周期不可逆:一旦进入 Runnable 状态就不能回到 New 状态;一旦被终止就不可能再有任何状态的变化。所以一个线程只能有一次 New 和 Terminated 状态,只有处于中间状态才可以相互转换
本文由作者按照 CC BY 4.0 进行授权