Java线程只有六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,全部来自Thread.State枚举;NEW是new未start,栈未分配;RUNNABLE表示JVM认为可运行(含OS就绪与执行),不区分二者;BLOCKED仅因争抢synchronized锁被阻塞;WAITING需显式唤醒(如wait/join/park);TIMED_WAITING为带超时的等待(sleep/wait/join/parkNanos);TERMINATED是run结束或异常退出,资源已回收。RUNNABLE不等于“正在CPU执行”,且无I/O阻塞对应状态——读文件或HTTP请求时仍显示RUNNABLE,因JVM不感知native I/O挂起;jstack中WAITING常因notify遗漏,BLOCKED多因synchronized范围过大,大量TIMED_WAITING可能暴露低效轮询。该模型是JVM为聚焦锁、等待、生命周期等关键干预点而做的实用取舍,非教学简化。

直接说清楚 Java 线程只有 六种状态,全部来自 Thread.State 枚举,这是 JDK 源码定义的唯一标准,不是教材或面试题自创的。重点不是背名字,而是讲清每种状态背后的线程行为、触发条件和 JVM 层面的真实含义。
六种状态必须按源码顺序说:NEW → RUNNABLE → BLOCKED → WAITING → TIMED_WAITING → TERMINATED
不加“就绪”“运行”“死亡”等非标准说法,避免混淆操作系统概念。比如:
- NEW:new 出 Thread 对象,但没调 start();此时连栈都没分配
- RUNNABLE:已调 start(),JVM 认为它“可运行”——包括正在 CPU 上执行,也包括在操作系统的就绪队列里排队;没有单独的“就绪”或“运行”状态
- BLOCKED:只发生在争抢 synchronized 锁时,且锁被别的线程持有;线程在等待进入同步块/方法
-
WAITING:调用
wait()、join()(无参)、LockSupport.park()后进入;必须靠其他线程显式唤醒(notify、unpark等),否则永远卡住 -
TIMED_WAITING:带超时的等待,如
sleep(1000)、wait(500)、join(200)、parkNanos();时间一到自动唤醒 - TERMINATED:run() 方法正常结束,或抛出未捕获异常退出;线程资源已被 JVM 回收,不可复用
强调两个关键认知偏差
很多候选人在这里翻车,可以主动点破:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- RUNNABLE 不等于“正在跑”:它包含 OS 层的“就绪”和“运行”两态,JVM 不区分——因为应用层排查问题时,你并不关心它此刻是排队还是真在执行,只关心它有没有卡在锁、IO 或等待上
-
没有“阻塞 I/O”对应的状态:比如读文件、发 HTTP 请求时线程挂起,
getState()仍返回 RUNNABLE;这是 JVM 的设计选择:底层由 native 线程执行 I/O,Java 线程状态不感知,所以不能靠 jstack 看状态判断 I/O 卡顿
用 jstack 实战佐证,让回答落地
线上出问题时,jstack 输出里的状态就是这些枚举值的英文。举例说明:
立即学习“Java免费学习笔记(深入)”;
- 看到一堆线程停在
java.lang.Object.wait(Native Method),状态是 WAITING → 很可能是 notify 漏了,或唤醒逻辑没覆盖全分支 - 大量线程状态为 BLOCKED,堆栈显示
at com.xxx.service.XxxService.update(LxxxService.java:45) - waiting to lock→ 锁竞争严重,得查 synchronized 范围是否过大 - 全是 TIMED_WAITING 且都在
java.lang.Thread.sleep(Native Method)→ 可能是轮询逻辑没改造成事件驱动,浪费 CPU 和线程资源
一句话收尾,体现深度
Java 线程状态模型不是教学简化的产物,而是 JVM 为平衡抽象清晰度与排查实用性做的取舍——它放弃操作系统级的精细划分,聚焦于开发者真正需要干预的并发点:锁、等待、生命周期。理解这点,才能从“背状态”升级到“看状态诊断问题”。

















