Java线程生命周期有6种JVM规范强制定义的状态:NEW(已创建未启动)、RUNNABLE(就绪或运行,含OS级阻塞)、BLOCKED(等待synchronized锁)、WAITING(无限期等待唤醒)、TIMED_WAITING(超时等待)、TERMINATED(已结束不可逆)。

Java线程的生命周期有6种明确状态,全部定义在 Thread.State 枚举中;不是“新建→运行→死亡”这种简化流程,中间存在多个可区分、可诊断的中间态。
NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED 各自代表什么
这六个状态是 JVM 规范强制定义的,反映线程在 OS 调度、锁竞争、同步等待等环节的真实处境:
-
NEW:线程对象已创建,但start()还没被调用;此时isAlive()返回false -
RUNNABLE:包含“就绪”和“正在 CPU 上执行”两种情况;注意:它不等于“正在运行”,IO 阻塞、synchronized 竞争失败时线程仍处于此状态(JVM 层面不可见阻塞) -
BLOCKED:线程正等待进入 synchronized 块/方法,因锁被其他线程持有而挂起;只与 monitor 锁相关,和ReentrantLock无关 -
WAITING:调用Object.wait()、Thread.join()或LockSupport.park()后无限期等待;需其他线程显式唤醒 -
TIMED_WAITING:带超时的等待,如Thread.sleep(1000)、Object.wait(500)、LockSupport.parkNanos() -
TERMINATED:线程run()方法正常结束或抛出未捕获异常后进入;不可逆,也不能复用
为什么 Thread.getState() 返回 RUNNABLE 却看不到 CPU 执行
这是最常被误解的一点:RUNNABLE 是 JVM 级状态,不反映 OS 级调度细节。以下情况线程仍是 RUNNABLE:
- 线程在执行纯计算逻辑,但被 OS 暂停调度(时间片用完)
- 线程发起系统调用(如
System.in.read()),进入内核态等待 IO 完成 - 线程尝试获取
synchronized锁失败,但尚未被判定为BLOCKED(JVM 需要确认锁确实被占用且竞争发生)
真正能说明“卡住”的是 BLOCKED 或 WAITING;仅靠 RUNNABLE 无法判断是否活跃,必须结合堆栈(Thread.getStackTrace())和外部监控(如 jstack)。
立即学习“Java免费学习笔记(深入)”;
如何通过 jstack 或代码准确识别 BLOCKED 和 WAITING 线程
jstack 输出里会明确标注状态,并附上下文线索:
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.Service.doWork(Service.java:42)
- waiting to lock <0x000000071a2b3c40> (a java.lang.Object)
- locked <0x000000071a2b3c50> (a java.lang.Object)代码中判断建议用 Thread.getState() + getStackTrace() 组合:
- 检查
state == Thread.State.BLOCKED且堆栈含at java.lang.Object.wait(Native Method)→ 实际是WAITING,别信表象 -
state == Thread.State.WAITING但堆栈显示at java.lang.Thread.sleep(Native Method)→ 不可能,说明 JVM 版本或诊断方式有误(sleep是TIMED_WAITING) - 对
ForkJoinPool中的工作线程,getState()常返回RUNNABLE,即使它正空闲等待任务——这是 ForkJoinWorkerThread 的设计使然,不能直接套用通用规则
状态切换不是原子的,getState() 只是快照;同一时刻多个线程处于 WAITING 并不奇怪,但若大量线程卡在 BLOCKED 且锁地址一致,基本可定位为锁瓶颈。真正难排查的是 RUNNABLE 状态下的长耗时操作——它不会暴露在线程 dump 里,得靠 profiler 抓 CPU 样本。








