线程的状态?
本文详细解析Java线程的6种核心状态(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED),包括状态定义、触发条件及转换路径。通过代码示例验证各状态转换过程,如NEW→RUNNABLE→TERMINATED的基础生命周期、synchronized锁竞争导致的BLOCKED状态、sleep()触发的TIMED_WAITING等。
·
线程状态(Java)
线程是操作系统调度的基本单位,其生命周期包含一系列状态转换。Java 中线程状态在 java.lang.Thread.State 枚举中明确定义(共 6 种核心状态),由 JVM 或线程操作自动触发切换,不可手动直接修改。本文结合原理、代码和场景,详细拆解每种状态。
一、核心概念:6 种线程状态定义
| 状态名称 | 核心含义 | 触发条件(进入方式) | 退出条件(切换方式) |
|---|---|---|---|
NEW(新建) |
线程对象已创建,但未调用 start() 方法(未启动,JVM 未分配执行资源)。 |
new Thread(...) 创建线程对象。 |
调用 thread.start() 方法,进入 RUNNABLE。 |
RUNNABLE(可运行) |
线程已启动,要么正在 CPU 上执行,要么等待 CPU 调度(就绪态)。 | 1. NEW 状态调用 start();2. 阻塞 / 等待状态恢复(如锁释放、超时、被唤醒)。 |
1. 执行完毕(run() 结束)→ TERMINATED;2. 竞争 synchronized 锁失败 → BLOCKED;3. 调用 sleep()/wait() 等 → TIMED_WAITING/WAITING。 |
BLOCKED(阻塞) |
线程因竞争 synchronized 锁 失败,被动等待锁释放(仅针对 synchronized)。 |
线程尝试进入 synchronized 代码块 / 方法,但锁被其他线程持有。 |
持有锁的线程释放锁,当前线程竞争到锁 → RUNNABLE。 |
WAITING(无限等待) |
线程主动放弃执行权,无超时时间,必须被其他线程唤醒才能恢复。 | 1. Object.wait()(无参,需在 synchronized 中);2. Thread.join()(无参);3. LockSupport.park()。 |
1. 其他线程调用 Object.notify()/notifyAll();2. join() 目标线程执行完毕;3. LockSupport.unpark(thread)。 |
TIMED_WAITING(计时等待) |
线程主动放弃执行权,有明确超时时间,超时后自动唤醒(也可手动唤醒)。 | 1. Thread.sleep(long ms);2. Object.wait(long ms);3. Thread.join(long ms);4. LockSupport.parkNanos()。 |
1. 超时自动唤醒 → RUNNABLE;2. 其他线程提前唤醒(notify()/unpark());3. join() 目标线程提前结束。 |
TERMINATED(终止) |
线程执行完毕(正常退出)或异常终止(未捕获异常)。 | 1. run() 方法执行完成;2. 线程抛出未捕获的 Exception/Error。 |
状态不可逆,终止后无法再次启动(调用 start() 抛异常)。 |
关键澄清
RUNNABLE不区分 “正在执行” 和 “等待 CPU 调度”:JVM 层面统一归为RUNNABLE,操作系统层面的 “就绪态” 和 “运行态” 被 Java 合并;BLOCKED仅针对synchronized锁:ReentrantLock等 Lock 框架的等待状态属于WAITING/TIMED_WAITING(通过Condition.await()实现);- 线程状态是 “被动切换”:开发者无法通过
setState()手动修改(该方法为protected,仅 JVM 可调用)。
二、线程状态切换流程图
[NEW]
↓ (调用 start())
[RUNNABLE]
↓ (竞争 synchronized 锁失败)
[BLOCKED]
↓ (获取锁成功)
[RUNNABLE]
↓ (调用 sleep()/wait(long)/join(long))
[TIMED_WAITING]
↓ (超时/被唤醒)
[RUNNABLE]
↓ (调用 wait()/join()/park())
[WAITING]
↓ (被 notify()/unpark()/join 目标结束)
[RUNNABLE]
↓ (run() 结束/抛异常)
[TERMINATED] (不可逆)
三、代码实现:逐个验证线程状态
环境准备
- JDK 8+(
Thread.State从 JDK 1.5 开始支持); - 开发工具:IDEA/Eclipse(通过
thread.getState()获取状态)。
1. 验证:NEW → RUNNABLE → TERMINATED(基础生命周期)
代码逻辑
创建线程对象(未启动)→ 调用 start() 启动 → 执行完毕后终止。
public class ThreadStateBasic {
public static void main(String[] args) throws InterruptedException {
// 1. 创建线程对象(未启动)→ NEW 状态
Thread thread = new Thread(() -> {
System.out.println("子线程执行中...");
// 简单执行逻辑,快速结束
}, "BasicThread");
// 打印初始状态:NEW
System.out.println("1. 线程创建后(未 start):" + thread.getState());
// 2. 启动线程 → RUNNABLE 状态
thread.start();
// 休眠 50ms,让线程有机会进入执行状态(避免主线程先打印)
Thread.sleep(50);
System.out.println("2. 线程启动后(执行中):" + thread.getState());
// 3. 主线程等待子线程执行完毕(join() 阻塞主线程)
thread.join();
// 打印终止状态:TERMINATED
System.out.println("3. 线程执行完毕后:" + thread.getState());
}
}
运行结果
1. 线程创建后(未 start):NEW
子线程执行中...
2. 线程启动后(执行中):RUNNABLE
3. 线程执行完毕后:TERMINATED
2. 验证:RUNNABLE → BLOCKED(synchronized 锁竞争)
代码逻辑
两个线程竞争同一 synchronized 锁,先启动的线程持有锁,后启动的线程竞争失败进入 BLOCKED。
public class ThreadStateBlocked {
// 共享锁对象(竞争目标)
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
// 线程 1:先获取锁,持有 2 秒
Thread thread1 = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println("线程 1:获取锁成功,执行中...");
Thread.sleep(2000); // 持有锁 2 秒,让线程 2 竞争失败
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "LockHolderThread");
// 线程 2:后获取锁,竞争失败进入 BLOCKED
Thread thread2 = new Thread(() -> {
synchronized (LOCK) {
System.out.println("线程 2:竞争锁成功,执行中...");
}
}, "LockWaitThread");
// 启动线程 1,确保其先获取锁
thread1.start();
Thread.sleep(500); // 休眠 500ms,让线程 1 稳定持有锁
// 启动线程 2,开始竞争锁
thread2.start();
Thread.sleep(500); // 休眠 500ms,让线程 2 进入 BLOCKED
// 打印线程 2 状态:BLOCKED
System.out.println("线程 2 状态(竞争锁失败):" + thread2.getState());
// 等待线程 1 释放锁(2 秒后),线程 2 竞争到锁
Thread.sleep(2000);
System.out.println("线程 2 状态(锁释放后):" + thread2.getState());
}
}
运行结果
线程 1:获取锁成功,执行中...
线程 2 状态(竞争锁失败):BLOCKED
线程 2:竞争锁成功,执行中...
线程 2 状态(锁释放后):TERMINATED
3. 验证:RUNNABLE → TIMED_WAITING(计时等待)
代码逻辑
线程调用 Thread.sleep(1000) 进入计时等待,期间状态为 TIMED_WAITING,超时后自动恢复 RUNNABLE。
public class ThreadStateTimedWaiting {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
System.out.println("子线程:进入计时等待(sleep 1 秒)...");
Thread.sleep(1000); // 触发 TIMED_WAITING
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程:计时等待结束,继续执行...");
}, "TimedThread");
thread.start();
Thread.sleep(200); // 休眠 200ms,确保线程进入 sleep 状态
// 打印计时等待期间状态:TIMED_WAITING
System.out.println("子线程状态(sleep 期间):" + thread.getState());
// 等待线程执行完毕
thread.join();
System.out.println("子线程状态(执行完毕):" + thread.getState());
}
}
运行结果
子线程:进入计时等待(sleep 1 秒)...
子线程状态(sleep 期间):TIMED_WAITING
子线程:计时等待结束,继续执行...
子线程状态(执行完毕):TERMINATED
其他计时等待场景
Object.wait(1000):释放锁,等待 1 秒(超时自动唤醒);Thread.join(1000):主线程等待子线程 1 秒(超时不再等待)。
4. 验证:RUNNABLE → WAITING(无限等待)
代码逻辑
线程 A 调用 Object.wait() 进入无限等待,线程 B 调用 Object.notify() 唤醒线程 A,恢复 RUNNABLE。
public class ThreadStateWaiting {
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
// 等待线程:调用 wait() 进入 WAITING
Thread waitThread = new Thread(() -> {
synchronized (LOCK) {
try {
System.out.println("等待线程:进入无限等待,等待被唤醒...");
LOCK.wait(); // 无参 wait(),释放锁,进入 WAITING
System.out.println("等待线程:被唤醒,继续执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "WaitThread");
// 唤醒线程:调用 notify() 唤醒等待线程
Thread notifyThread = new Thread(() -> {
synchronized (LOCK) {
try {
Thread.sleep(1000); // 确保等待线程先进入 WAITING
System.out.println("唤醒线程:准备唤醒等待线程...");
LOCK.notify(); // 唤醒一个等待在 LOCK 上的线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "NotifyThread");
waitThread.start();
Thread.sleep(500); // 休眠 500ms,确保等待线程进入 wait()
// 打印等待线程状态:WAITING
System.out.println("等待线程状态:" + waitThread.getState());
notifyThread.start();
waitThread.join(); // 等待等待线程执行完毕
System.out.println("等待线程最终状态:" + waitThread.getState());
}
}
运行结果
等待线程:进入无限等待,等待被唤醒...
等待线程状态:WAITING
唤醒线程:准备唤醒等待线程...
等待线程:被唤醒,继续执行...
等待线程最终状态:TERMINATED
5. 验证:TERMINATED 状态不可逆
代码逻辑
线程终止后,尝试再次调用 start() 会抛出 IllegalThreadStateException。
public class ThreadStateTerminated {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("子线程执行中...");
}, "TerminatedThread");
thread.start();
thread.join(); // 等待线程终止
System.out.println("线程终止后状态:" + thread.getState()); // TERMINATED
// 尝试重新启动终止的线程(触发异常)
try {
thread.start();
} catch (IllegalThreadStateException e) {
System.out.println("异常信息:" + e.getMessage());
}
}
}
运行结果
子线程执行中...
线程终止后状态:TERMINATED
异常信息:illegal thread state
四、核心 API 与状态的关联
| API 方法 | 作用 | 状态切换影响 |
|---|---|---|
Thread.start() |
启动线程(仅能调用一次) | NEW → RUNNABLE |
Thread.sleep(long ms) |
线程休眠(不释放锁) | RUNNABLE → TIMED_WAITING |
Object.wait() |
无限等待(释放锁,需 synchronized) |
RUNNABLE → WAITING |
Object.wait(long ms) |
计时等待(释放锁,需 synchronized) |
RUNNABLE → TIMED_WAITING |
Object.notify() |
唤醒一个等待在该对象上的线程 | WAITING/TIMED_WAITING → RUNNABLE |
Object.notifyAll() |
唤醒所有等待在该对象上的线程 | WAITING/TIMED_WAITING → RUNNABLE |
Thread.join() |
主线程等待子线程结束(无限等待) | 主线程:RUNNABLE → WAITING |
Thread.join(long ms) |
主线程等待子线程(计时等待) | 主线程:RUNNABLE → TIMED_WAITING |
LockSupport.park() |
暂停线程(无超时,不释放锁) | RUNNABLE → WAITING |
LockSupport.unpark(Thread t) |
唤醒指定线程 | WAITING/TIMED_WAITING → RUNNABLE |
Thread.getState() |
查询线程当前状态 | 无(仅查询) |
五、常见面试题(线程状态核心)
1. WAITING 和 TIMED_WAITING 的区别?
- 核心差异:是否有超时时间;
WAITING:无超时,必须依赖其他线程唤醒(notify()/unpark());TIMED_WAITING:有超时时间,超时后自动唤醒,也可手动提前唤醒。
2. BLOCKED 和 WAITING 的区别?
| 维度 | BLOCKED |
WAITING |
|---|---|---|
| 触发原因 | 竞争 synchronized 锁失败(被动) |
调用 wait()/join() 等(主动) |
| 锁持有情况 | 未持有锁(无需释放) | 持有锁时会释放(如 wait()) |
| 恢复条件 | 锁释放并竞争成功 | 被唤醒或 join() 目标线程结束 |
3. sleep() 和 wait() 的区别?
| 维度 | Thread.sleep(long) |
Object.wait() |
|---|---|---|
| 所属类 | Thread 静态方法 |
Object 实例方法 |
| 锁释放 | 不释放任何锁 | 释放当前对象锁(需在 synchronized 中) |
| 唤醒方式 | 超时自动唤醒 | 需 notify()/notifyAll() 唤醒 |
| 使用场景 | 控制线程执行节奏(如暂停 1 秒) | 线程间通信(如生产者 - 消费者模型) |
4. 线程终止后能重新启动吗?
- 不能。线程进入
TERMINATED状态后,生命周期结束,再次调用start()会抛出IllegalThreadStateException; - 若需重复执行任务,需重新创建
Thread对象。
六、总结
线程状态是理解多线程生命周期的核心,关键在于:
- 记住 6 种状态的触发条件和切换路径;
- 掌握核心 API 对状态的影响(如
sleep()触发TIMED_WAITING); - 区分易混淆状态(如
BLOCKEDvsWAITING)。
实际开发中,线程状态常用于问题排查(如死锁时线程处于 BLOCKED)、性能优化(如避免线程长期 WAITING),掌握这些知识能写出更稳健的多线程代码。
更多推荐


所有评论(0)