线程的生命周期
在 Java 中,线程的生命周期包含6 种状态,这些状态定义在Thread.State
枚举中,状态之间会根据操作发生转换。理解线程状态转换是掌握多线程编程的基础。
一、线程的 6 种状态
根据 Java 官方文档,线程的状态分为以下 6 种:
状态名称 | 说明 |
---|---|
NEW(新建) | 线程对象已创建,但未调用start() 方法(尚未启动)。 |
RUNNABLE(可运行) | 线程已启动,正在 JVM 中运行,或等待 CPU 调度(包含传统 OS 中的 “运行” 和 “就绪” 状态)。 |
BLOCKED(阻塞) | 线程等待获取 synchronized 锁(因竞争锁而被阻塞)。 |
WAITING(无限等待) | 线程无限期等待其他线程的特定操作(如notify() ),无超时时间。 |
TIMED_WAITING(计时等待) | 线程等待指定时间后自动唤醒(如sleep(1000) 、wait(1000) )。 |
TERMINATED(终止) | 线程执行完毕(run() 方法结束)或因异常终止。 |
二、状态转换流程图
[NEW] ↓ (调用start())
[RUNNABLE] ↓ (获取锁失败) ↓ (释放锁后重新竞争)
[BLOCKED] --------------------→ [RUNNABLE]↑ ↓ (调用wait()/join()等)| [WAITING]| ↓ (被notify()/中断)| → [RUNNABLE]| ↓ (调用wait(超时)/sleep(超时)等)| [TIMED_WAITING]| ↓ (超时/被notify()/中断)└----------------------------→ [RUNNABLE]↓ (run()执行完毕/异常)[TERMINATED]
三、详细转换说明
1. NEW → RUNNABLE
- 触发条件:调用线程对象的
start()
方法(不能重复调用,否则抛IllegalThreadStateException
)。 - 示例:
Thread thread = new Thread(() -> {}); System.out.println(thread.getState()); // 输出:NEW thread.start(); System.out.println(thread.getState()); // 可能输出:RUNNABLE(取决于调度)
2. RUNNABLE ↔ BLOCKED
- RUNNABLE → BLOCKED:线程尝试获取
synchronized
锁时,若锁被其他线程持有,则进入BLOCKED
状态。 - BLOCKED → RUNNABLE:持有锁的线程释放锁后,当前线程竞争到锁,重新进入
RUNNABLE
。 - 示例:
Object lock = new Object();// 线程1先获取锁 Thread t1 = new Thread(() -> {synchronized (lock) {try { Thread.sleep(1000); } // 持有锁休眠catch (InterruptedException e) {}} });// 线程2后尝试获取锁,会进入BLOCKED Thread t2 = new Thread(() -> {synchronized (lock) { // 此时t1持有锁,t2进入BLOCKEDSystem.out.println("获取锁成功");} });t1.start(); Thread.sleep(100); // 确保t1先执行 t2.start(); System.out.println(t2.getState()); // 输出:BLOCKED
3. RUNNABLE ↔ WAITING
RUNNABLE → WAITING:
- 调用
object.wait()
(必须在synchronized
块中,释放锁并等待)。 - 调用
thread.join()
(等待目标线程执行完毕)。 - 调用
LockSupport.park()
(无参数版本,阻塞当前线程)。
- 调用
WAITING → RUNNABLE:
- 其他线程调用
object.notify()
或object.notifyAll()
(唤醒后需重新竞争锁)。 - 被
join()
的目标线程执行完毕。 - 其他线程调用
LockSupport.unpark(thread)
。
- 其他线程调用
示例(
wait()
/notify()
):Object lock = new Object(); Thread t = new Thread(() -> {synchronized (lock) {try {lock.wait(); // 释放锁,进入WAITING} catch (InterruptedException e) {}} }); t.start(); Thread.sleep(100); System.out.println(t.getState()); // 输出:WAITING// 其他线程唤醒 new Thread(() -> {synchronized (lock) {lock.notify(); // 唤醒t线程} }).start();
4. RUNNABLE ↔ TIMED_WAITING
RUNNABLE → TIMED_WAITING:
- 调用
Thread.sleep(long millis)
(不释放锁,仅暂停执行)。 - 调用
object.wait(long timeout)
(释放锁,等待指定时间)。 - 调用
thread.join(long millis)
(限时等待目标线程)。 - 调用
LockSupport.parkNanos()
或LockSupport.parkUntil()
(限时阻塞)。
- 调用
TIMED_WAITING → RUNNABLE:
- 等待时间超时。
- 其他线程调用
notify()
/notifyAll()
(需重新竞争锁)。 - 线程被中断(
interrupt()
)。
示例(
sleep()
):Thread t = new Thread(() -> {try {Thread.sleep(1000); // 进入TIMED_WAITING} catch (InterruptedException e) {} }); t.start(); Thread.sleep(100); System.out.println(t.getState()); // 输出:TIMED_WAITING
5. RUNNABLE → TERMINATED
- 触发条件:
- 线程的
run()
方法正常执行完毕。 - 线程执行中抛出未捕获的异常(导致
run()
方法终止)。
- 线程的
- 示例:
Thread t = new Thread(() -> {}); t.start(); Thread.sleep(100); // 等待线程执行完毕 System.out.println(t.getState()); // 输出:TERMINATED
四、关键注意点
RUNNABLE
的特殊含义:Java 中的RUNNABLE
包含两种情况:- 线程正在 CPU 上执行(运行中)。
- 线程就绪,等待 CPU 调度(未运行但可立即执行)。这与操作系统层面的 “运行” 和 “就绪” 状态不同,是 Java 对线程状态的简化抽象。
BLOCKED
与WAITING
的区别:BLOCKED
:等待synchronized 锁(被动等待,无需主动唤醒)。WAITING
:等待其他线程的特定操作(如notify()
,需主动唤醒)。
状态不可逆:线程进入
TERMINATED
后,无法再回到其他状态(即使调用start()
也会报错)。sleep()
与wait()
的状态差异:sleep()
:线程进入TIMED_WAITING
,不释放锁。wait()
:线程进入WAITING
或TIMED_WAITING
,释放锁。
总结
线程的生命周期是多线程调度的核心,6 种状态的转换反映了线程从创建到终止的完整过程:
- 从
NEW
启动到RUNNABLE
,再根据锁竞争、等待操作进入BLOCKED
/WAITING
/TIMED_WAITING
,最终执行完毕进入TERMINATED
。
理解这些状态转换,有助于诊断线程死锁、阻塞等问题,写出更可靠的多线程程序。