【线程的几种状态】
💡 摘要:你是否曾被 RUNNABLE 状态迷惑——它为何包含了“阻塞”?
是否在排查线程池时,看到 WAITING 状态却不知其因?
Java 线程的 6 种状态(NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED)
不仅是枚举值,更是理解 JVM 线程模型、锁竞争、协作机制的钥匙。
本文将从基础到深入,结合代码、图解、jstack 实战,
彻底讲清每种状态的含义、转换条件、监控方法与常见误区。
文末附高清状态转换图与面试高频问题清单,助你真正掌握多线程核心基础。
一、从“线程是什么”说起在深入状态前,先明确:线程是 CPU 调度的基本单位。
一个 Java 程序启动,JVM 会创建一个 main 线程。你可以创建更多线程,让任务并发执行。但 CPU 核心数有限,操作系统需要在多个线程间快速切换,造成“同时运行”的假象。 🔑 线程状态的本质:
描述线程在 JVM 和操作系统调度 下的当前所处阶段。
二、Java 线程状态全景Java 定义了 6 种线程状态,位于 java.lang.Thread.State 枚举中:
代码语言:javascript代码运行次数:0运行复制public enum State {
NEW, // 新建
RUNNABLE, // 可运行
BLOCKED, // 阻塞
WAITING, // 无限期等待
TIMED_WAITING, // 限期等待
TERMINATED // 终止
} ✅ 重要提示:
这是 JVM 层面的抽象状态,与操作系统的线程状态(如 running, ready, waiting)不完全等价。
特别是 RUNNABLE,它包含了操作系统的 ready(就绪)和 running(运行)两种状态。
三、逐个击破:6 种状态详解1. NEW(新建)——生命的起点含义:线程对象已创建,但尚未调用 start()。特点: 线程还未被 JVM 纳入调度。此时调用 getState() 可返回 NEW。代码语言:javascript代码运行次数:0运行复制Thread t = new Thread(() -> System.out.println("Hello"));
System.out.println(t.getState()); // 输出:NEW
t.start(); // 调用后,状态将改变 ✅ 生命周期的第一步。
2. RUNNABLE(可运行)——CPU 的“候选者”含义:线程正在 JVM 中执行,或正等待 CPU 分配时间片。关键理解(易错点!): RUNNABLE ≠ 正在运行!它包含两种 OS 状态: ready:已准备好,排队等 CPUrunning:正在 CPU 上执行I/O 阻塞(如读文件、网络请求)也属于 RUNNABLE!
因为 JVM 无法区分线程是在计算还是在等 I/O。代码语言:javascript代码运行次数:0运行复制// 示例 1:CPU 密集型(running)
Thread cpuTask = new Thread(() -> {
long sum = 0;
for (int i = 0; i < Integer.MAX_VALUE; i++) sum++;
}); // 状态:RUNNABLE
// 示例 2:I/O 操作(看似阻塞,但状态仍是 RUNNABLE)
Thread ioTask = new Thread(() -> {
try (FileInputStream fis = new FileInputStream("large.log")) {
fis.readAllBytes(); // I/O 阻塞中,但 getState() == RUNNABLE
} catch (IOException e) { e.printStackTrace(); }
}); 🔥 面试常考:为什么 I/O 阻塞的线程状态是 RUNNABLE?
答:JVM 的线程状态模型未细分 I/O 阻塞,只要线程未进入 wait/sleep/等锁,就视为“可运行”。
3. BLOCKED(阻塞)——锁的竞争者含义:线程试图获取一个监视器锁(monitor)失败,等待进入 synchronized 块/方法。触发场景: 尝试进入一个被其他线程持有的 synchronized 方法或代码块。从 WAITING 状态被唤醒后,重新竞争锁失败。代码语言:javascript代码运行次数:0运行复制Object lock = new Object();
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.println("t1 持有锁,开始休眠...");
try { Thread.sleep(3000); } catch (InterruptedException e) {}
System.out.println("t1 释放锁");
}
});
Thread t2 = new Thread(() -> {
System.out.println("t2 尝试获取锁...");
synchronized (lock) { // t2 在此处进入 BLOCKED 状态
System.out.println("t2 终于获得锁!");
}
});
t1.start();
Thread.sleep(500);
t2.start(); // t2 会因锁被 t1 占有而进入 BLOCKED ✅ BLOCKED 的核心是 synchronized 锁的竞争。
4. WAITING(无限期等待)——协作的等待者含义:线程无限期等待另一个线程执行特定操作来唤醒它。进入方式(调用以下方法,无超时参数): Object.wait()Thread.join()(无超时)LockSupport.park()唤醒方式: Object.notify() / notifyAll()被其他线程中断(interrupt())LockSupport.unpark()代码语言:javascript代码运行次数:0运行复制Thread t1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("t1 进入等待...");
lock.wait(); // t1 进入 WAITING 状态,释放锁
System.out.println("t1 被唤醒!");
} catch (InterruptedException e) { e.printStackTrace(); }
}
});
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("t2 即将唤醒 t1");
lock.notify(); // 唤醒 t1
}
});
t1.start();
Thread.sleep(1000);
t2.start(); ✅ WAITING 状态的线程不消耗 CPU,是线程间协作的基础。
5. TIMED_WAITING(限期等待)——有耐心的等待者含义:线程等待一段时间,时间到后自动唤醒。进入方式(带超时参数): Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos(long nanos)唤醒方式: 超时自动唤醒被中断显式唤醒(如 notify)代码语言:javascript代码运行次数:0运行复制Thread t = new Thread(() -> {
try {
System.out.println("开始休眠 2 秒...");
Thread.sleep(2000); // 进入 TIMED_WAITING
System.out.println("休眠结束!");
} catch (InterruptedException e) { e.printStackTrace(); }
}); ✅ TIMED_WAITING 是 WAITING 的“限时版”。
6. TERMINATED(终止)——生命的终点含义:线程的 run() 方法已执行完毕,或因未捕获异常而退出。特点: 线程对象依然存在,但已“死亡”。无法再次 start()(会抛 IllegalThreadStateException)。代码语言:javascript代码运行次数:0运行复制Thread t = new Thread(() -> {
System.out.println("任务完成");
});
t.start();
t.join(); // 等待 t 结束
System.out.println(t.getState()); // 输出:TERMINATED四、线程状态转换图(高清版) ✅ 核心逻辑:
RUNNABLE 是中心枢纽。BLOCKED 专为 synchronized 锁设计。WAITING 和 TIMED_WAITING 用于线程协作。五、实战:如何监控线程状态?1. 代码中获取状态:getState()代码语言:javascript代码运行次数:0运行复制Thread t = new Thread(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
});
System.out.println("NEW: " + t.getState());
t.start();
System.out.println("启动后: " + t.getState()); // 可能是 RUNNABLE
Thread.sleep(100);
System.out.println("休眠中: " + t.getState()); // TIMED_WAITING
t.join();
System.out.println("结束后: " + t.getState()); // TERMINATED ⚠️ 注意:getState() 是瞬时快照,可能不精确。
2. 生产环境利器:jstack代码语言:javascript代码运行次数:0运行复制# 1. 查找 Java 进程 ID
jps
# 2. 输出线程栈和状态
jstack
代码语言:javascript代码运行次数:0运行复制"main" #1 prio=5 os_prio=0 cpu=15.62ms elapsed=4.32s tid=0x000002aab8019000 nid=0x5a44 waiting on condition [0x000000b8c5fff000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(java.base@17.0.8/Native Method)
at Main.main(Main.java:5) 🔥 jstack 是诊断死锁、性能瓶颈的必备工具。
3. 线程池中的状态线程池中的工作线程(worker)在空闲时会调用 workQueue.take(),进入 WAITING 状态,等待新任务。
六、常见问题与面试解析❓1. RUNNABLE 为什么包含 I/O 阻塞? 答:JVM 的线程状态模型是简化的。
只要线程没有调用 wait、sleep 或进入 synchronized 竞争锁,
即使它在等待 I/O,JVM 也认为它处于“可运行”状态。
这也是 jstack 中大量线程显示 RUNNABLE 的原因。
❓2. BLOCKED 和 WAITING 的本质区别? 答:
BLOCKED:因锁竞争失败而被动阻塞(synchronized)。WAITING:因主动协作而等待(wait/join),需其他线程显式唤醒。关键:WAITING 会释放锁,BLOCKED 不会。❓3. sleep() 和 wait() 的状态与锁行为? 答:
方法状态是否释放锁调用位置sleep()TIMED_WAITING否任意位置wait()WAITING是必须在 synchronized 块内
❓4. 线程终止后能重启吗? 答:不能。
线程是“一次性”的。终止后再次 start() 会抛 IllegalThreadStateException。
应使用线程池或创建新线程。
❓5. 如何判断线程是否活跃? 答:使用 thread.isAlive()。
当线程处于 RUNNABLE, BLOCKED, WAITING, TIMED_WAITING 时返回 true。
七、总结状态
触发条件
关键点
NEW
new Thread()
未启动
RUNNABLE
start() / I/O / 计算
包含就绪、运行、I/O 阻塞
BLOCKED
synchronized 竞争锁
锁竞争
WAITING
wait() / join() (无超时)
主动协作,需唤醒
TIMED_WAITING
sleep() / wait(timeout)
限期等待,自动唤醒
TERMINATED
run() 结束
生命周期结束
✅ 掌握线程状态,是理解并发编程的第一步。
动手实践,结合 jstack 分析,你将对多线程有更深的理解。