线程有几种状态?状态之间是如何流转的?
结论先行:Java 线程有 6 种状态,定义在 Thread.State 枚举里:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。注意它和操作系统的线程状态不是一一对应的——JVM 故意不区分"就绪"和"运行",所以 jstack 里永远看不到 RUNNING。
1. 六种状态一览
| 状态 | 怎么进入 | 是否释放已持有的锁 | 怎么离开 |
|---|---|---|---|
NEW | new Thread() 之后、start() 之前 | 尚未持有 | 调 start() |
RUNNABLE | 调用了 start(),正在 JVM 中执行 | 不释放 | 被阻塞 / 等待 / 结束 |
BLOCKED | 等待 synchronized 的 monitor 进入锁 | 还没拿到,谈不上释放 | 抢到锁 → RUNNABLE |
WAITING | wait()、join()、park(),且无限等 | wait() 会释放,join/park 不涉及 | notify/notifyAll/被 join 的线程结束 |
TIMED_WAITING | sleep(n)、wait(n)、join(n)、parkNanos | sleep(n) 不释放,wait(n) 释放 | 超时或被唤醒 |
TERMINATED | run() 正常返回或抛出未捕获异常 | 已释放 | 不可逆 |
2. 状态流转图
NEW ──start()──▶ RUNNABLE
RUNNABLE ──等 synchronized 进入锁──▶ BLOCKED ──获得锁──▶ RUNNABLE
RUNNABLE ──wait()/join()/park()(无超时)──▶ WAITING
WAITING ──notify()/notifyAll()/被 join 线程结束──▶ RUNNABLE
RUNNABLE ──sleep(n)/wait(n)/join(n)/parkNanos──▶ TIMED_WAITING
TIMED_WAITING ──超时或提前被唤醒──▶ RUNNABLE
RUNNABLE ──run() 返回或抛异常──▶ TERMINATED
三个容易记混的点:
- 只有等
synchronized才是 BLOCKED;等ReentrantLock、等Condition、等 IO 都是WAITING或TIMED_WAITING; sleep()不释放锁,wait()释放锁——这是 wait/sleep 最本质的差别;- 线程一旦
TERMINATED,再调start()会抛IllegalThreadStateException,不能复活。
3. 用代码把状态"抓"出来
public class StateDemo {
private static final Object LOCK = new Object();
public static void main(String[] args) throws Exception {
Thread t = new Thread(() -> {
synchronized (LOCK) {
try {
LOCK.wait(); // 进入 WAITING 并释放 LOCK
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}, "state-demo");
System.out.println(t.getState()); // NEW
t.start();
Thread.sleep(100);
System.out.println(t.getState()); // WAITING
synchronized (LOCK) { LOCK.notifyAll(); }
t.join();
System.out.println(t.getState()); // TERMINATED
}
}
线上更常用的是 jstack,它把状态翻译成了人话:
"state-demo" #23 prio=5 os_prio=0 tid=0x00007f... nid=0x6a2c in Object.wait()
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at com.demo.StateDemo.lambda$main$0(StateDemo.java:11)
- waiting on <0x000000076ab622b8> (a java.lang.Object)
- locked <0x000000076ab622a8> (a java.lang.Object)
locked 表示已持有、waiting on 表示在等它,一眼就能定位"谁堵住了谁"。
4. 常见追问
追问一:为什么没有 RUNNING 状态?
因为"是否正在占用 CPU"由操作系统调度,JVM 不去实时同步这个信息(同步成本高且没有意义)。所以 RUNNABLE 是一个大杂烩:可能在跑、可能在就绪队列里排队、也可能阻塞在 read() 这类 native IO 上——后者在读 socket 时同样显示 RUNNABLE,排查时不要被它骗了。
追问二:BLOCKED 和 WAITING 的区别?
BLOCKED 是被动的:想进 synchronized 但锁在别人手里,只能等;WAITING 是主动的:自己调了 wait()/park() 让出执行权,且 wait() 会释放锁。前者是"抢不到",后者是"自己不干了"。
追问三:Thread.sleep 与 Object.wait 有什么区别?
| 对比项 | sleep(n) | wait(n) |
|---|---|---|
| 所属类 | Thread 静态方法 | Object 实例方法 |
| 是否释放锁 | 不释放 | 释放 |
| 是否必须在同步块 | 不需要 | 必须持有该对象的 monitor |
| 唤醒方式 | 只有超时 | 超时或被 notify |
| 状态 | TIMED_WAITING | TIMED_WAITING 或 WAITING |
追问四:线程状态能用于业务判断吗?
不能。getState() 是瞬时快照,读到 RUNNABLE 下一纳秒就可能变。它只适合排查与监控,不能当业务条件使用。
小结
记状态不要死背,抓住"等锁是 BLOCKED,等信号是 WAITING,等时间是 TIMED_WAITING"这条主线,再补一句"wait 释放锁、sleep 不释放",线程状态这块的追问基本都能接住。