💡 摘要:你是否曾被
RUNNABLE状态迷惑——它为何包含了“阻塞”? 是否在排查线程池时,看到WAITING状态却不知其因? Java 线程的 6 种状态(NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED) 不仅是枚举值,更是理解 JVM 线程模型、锁竞争、协作机制的钥匙。 本文将从基础到深入,结合代码、图解、jstack实战, 彻底讲清每种状态的含义、转换条件、监控方法与常见误区。 文末附高清状态转换图与面试高频问题清单,助你真正掌握多线程核心基础。
在深入状态前,先明确:线程是 CPU 调度的基本单位。
main 线程。🔑 线程状态的本质: 描述线程在 JVM 和操作系统调度 下的当前所处阶段。
Java 定义了 6 种线程状态,位于 java.lang.Thread.State 枚举中:
public enum State {
NEW, // 新建
RUNNABLE, // 可运行
BLOCKED, // 阻塞
WAITING, // 无限期等待
TIMED_WAITING, // 限期等待
TERMINATED // 终止
}✅ 重要提示: 这是 JVM 层面的抽象状态,与操作系统的线程状态(如 running, ready, waiting)不完全等价。 特别是
RUNNABLE,它包含了操作系统的 ready(就绪)和 running(运行)两种状态。
start()。getState() 可返回 NEW。Thread t = new Thread(() -> System.out.println("Hello"));
System.out.println(t.getState()); // 输出:NEW
t.start(); // 调用后,状态将改变✅ 生命周期的第一步。
RUNNABLE ≠ 正在运行!RUNNABLE!
因为 JVM 无法区分线程是在计算还是在等 I/O。// 示例 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/等锁,就视为“可运行”。
synchronized 块/方法。synchronized 方法或代码块。WAITING 状态被唤醒后,重新竞争锁失败。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锁的竞争。
Object.wait()Thread.join()(无超时)LockSupport.park()Object.notify() / notifyAll()interrupt())LockSupport.unpark()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,是线程间协作的基础。
Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos(long nanos)notify)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的“限时版”。
run() 方法已执行完毕,或因未捕获异常而退出。start()(会抛 IllegalThreadStateException)。Thread t = new Thread(() -> {
System.out.println("任务完成");
});
t.start();
t.join(); // 等待 t 结束
System.out.println(t.getState()); // 输出:TERMINATED
✅ 核心逻辑:
RUNNABLE 是中心枢纽。BLOCKED 专为 synchronized 锁设计。WAITING 和 TIMED_WAITING 用于线程协作。getState()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()是瞬时快照,可能不精确。
jstack# 1. 查找 Java 进程 ID
jps
# 2. 输出线程栈和状态
jstack <pid>输出片段:
"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是诊断死锁、性能瓶颈的必备工具。
线程池中的工作线程(worker)在空闲时会调用 workQueue.take(),进入 WAITING 状态,等待新任务。
答:JVM 的线程状态模型是简化的。 只要线程没有调用
wait、sleep或进入synchronized竞争锁, 即使它在等待 I/O,JVM 也认为它处于“可运行”状态。 这也是jstack中大量线程显示RUNNABLE的原因。
答:
BLOCKED:因锁竞争失败而被动阻塞(synchronized)。WAITING:因主动协作而等待(wait/join),需其他线程显式唤醒。WAITING 会释放锁,BLOCKED 不会。答: 方法状态是否释放锁调用位置
sleep()TIMED_WAITING否任意位置wait()WAITING是必须在synchronized块内
答:不能。 线程是“一次性”的。终止后再次
start()会抛IllegalThreadStateException。 应使用线程池或创建新线程。
答:使用
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分析,你将对多线程有更深的理解。