概述
在Java中线程的生命周期中共有6种不同的状态:
- 初始状态
(NEW)
:线程已经构建,尚未启动。 → 线程刚创建时的状态。 - 运行状态
(RUNNABLE)
:包括(可运行)就绪(READY)
和运行中(RUNNING)
两种状态,统称为运行状态。 → 线程创建后调用#start()方法即可进入该状态。 - 阻塞状态
(BLOCKED)
:线程被锁阻塞。 - 等待状态
(WAITING)
:线程需要等待其他线程做出特定动作(通知或中断)。 - 超时等待状态
(TIME_WAITING)
:不同于等待状态,超时等待状态可以在指定的时间自行返回。 - 终止状态
(TERMINATED)
:当前线程已经执行完毕。
在操作系统层面,Java 线程中的
BLOCKED、WAITING、TIMED_WAITING
是同一种状态,即休眠状态。也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永远没有 CPU 的使用权。
状态转换图如下所示:
注意上面的图有个错误,wait到 runnable状态的转换中,join实际上是Thread类的方法,但这里写成了Object
RUNNABLE 与 BLOCKED 的状态转换
只有「线程等待 synchronized 的隐式锁」这种场景才会发生上述状态转换。当等待的线程获得 synchronized 隐式锁时,就又会从 BLOCKED 转换到 RUNNABLE 状态。
Q:线程调用阻塞式 API 时,是否会转换到 BLOCKED 状态呢?
A:在操作系统层面,线程是会转换到休眠状态的,但是在 JVM 层面,Java 线程的状态不会发生变化。因为在 JVM 看来,等待 CPU 使用权(操作系统层面此时处于可执行状态)与等待 I/O(操作系统层面此时处于休眠状态)没有区别,都是在等待某个资源,所以都归入了 RUNNABLE 状态。
而我们平时所谓的 Java 在调用阻塞式 API 时,线程会阻塞,指的是操作系统线程的状态,并不是 Java 线程的状态。
RUNNABLE 与 WAITING 的状态转换
- 获得 synchronized 隐式锁的线程,调用无参数的
Object.wait()
方法。 - 调用无参数的
Thread.join()
方法。其中的 join() 是一种线程同步方法,例如有一个线程对象 thread A,当调用 A.join() 的时候,执行这条语句的线程会等待 thread A 执行完,而等待中的这个线程,其状态会从 RUNNABLE 转换到 WAITING。当线程 thread A 执行完,原来等待它的线程又会从 WAITING 状态转换到 RUNNABLE。 - 调用
LockSupport.park()
方法。→ JUC中的锁,都是基于它实现的。调用 LockSupport.park() 方法,当前线程会阻塞,线程的状态会从 RUNNABLE 转换到 WAITING。调用 LockSupport.unpark(Thread thread) 可唤醒目标线程,目标线程的状态又会从 WAITING 状态转换到 RUNNABLE。
RUNNABLE 与 TIMED_WAITING 的状态转换
- 调用带超时参数的
Thread.sleep(long millis)
方法 - 获得 synchronized 隐式锁的线程,调用带超时参数的
Object.wait(long timeout)
方法 - 调用带超时参数的
Thread.join(long millis)
方法 - 调用带超时参数的
LockSupport.parkNanos(Object blocker, long deadline)
方法 - 调用带超时参数的
LockSupport.parkUntil(long deadline)
方法。
RUNNABLE 与 TERMINATED 的状态转换
- 线程执行完 run() 方法后,会自动转换到 TERMINATED 状态。
- 如果执行 run() 方法的时候异常抛出,也会导致线程终止。
- 强制终止的方法:
stop()、suspend()、resume()