java线程6种状态精确对应jvm调度行为:new(未start)、runnable(可调度,含就绪与运行)、blocked(争抢synchronized锁)、waiting/timed_waiting(等待唤醒或超时)、terminated(run结束),状态切换受os和jvm共同影响,非代码完全可控。

Java线程的6种状态到底对应什么实际行为
Java线程不是靠“运行中”这种模糊描述判断状态的,Thread.State 的6个枚举值直接映射到JVM线程调度器的真实决策点。比如 WAITING 和 TIMED_WAITING 看似只差一个超时参数,但前者必须依赖其他线程显式唤醒(如 Object.notify()),后者可能自己醒来(如 Thread.sleep(1000)),一旦混淆就容易写出让线程永远卡住的代码。
-
NEW:线程对象已创建,但start()还没调用;此时调用join()会立即返回,不会阻塞 -
RUNNABLE:不等于“正在CPU上执行”,而是“可被调度”——包括在操作系统就绪队列里排队等待CPU,也包括正在执行字节码 -
BLOCKED:只发生在进入synchronized块/方法时抢不到锁,和I/O阻塞、sleep无关;可通过jstack看到 “waiting to lock0x...” -
WAITING/TIMED_WAITING:由Object.wait()、Thread.join()、LockSupport.park()等触发,注意Thread.sleep()是TIMED_WAITING,不是WAITING -
TERMINATED:线程 run() 方法正常结束、抛出未捕获异常、或被stop()(已废弃)终止后进入;此后调用isAlive()返回false,但状态字段仍为TERMINATED
state == TERMINATED 不代表线程资源已释放
很多人以为线程进入 TERMINATED 状态就万事大吉,其实 JVM 只是标记它结束了,栈帧、本地变量、持有的 monitor 锁(如果有的话)是否清理干净,取决于 GC 是否回收该 Thread 对象本身。常见陷阱是在线程内启动了守护线程、注册了 shutdown hook、或持有静态集合引用,导致线程对象无法被回收,进而让整个线程上下文残留内存。
- 线程退出后,若它创建的
ThreadLocal变量没手动remove(),可能引发内存泄漏——尤其是在线程池复用场景下 -
TERMINATED线程对象仍可调用getStackTrace(),但返回空数组;而getState()始终返回TERMINATED,哪怕对象已被 GC 标记 - 不要依赖
isAlive() == false来判断“线程彻底消失”,它只说明 run() 已退出;真正要确认资源释放,得看 GC 日志或堆直方图里java.lang.Thread实例数是否下降
从 NEW 到 TERMINATED 的转换不是全由代码控制
你写了 thread.start(),但线程未必立刻变成 RUNNABLE;你写了 thread.interrupt(),它也不一定立刻进 TERMINATED。中间穿插着操作系统线程创建开销、JVM 线程调度策略、甚至 GC 暂停的影响。最典型的反直觉点:线程在 RUNNABLE 状态下可能被 OS 调度器长时间挂起,而 JVM 依然认为它是“可运行”的。
-
start()调用后,JVM 需调用 OS API 创建原生线程,这步失败会导致IllegalThreadStateException,但状态仍为NEW - 线程在
BLOCKED或WAITING时被中断,会抛出InterruptedException并转为RUNNABLE,而不是直接终结 - 调用
interrupt()对TERMINATED线程无效,但对NEW线程会设置中断标志(isInterrupted()返回true),这个标志在start()后才可能生效 - JDK 19+ 引入虚拟线程(
Thread.ofVirtual()),其状态机与平台线程不同:NEW→RUNNABLE→TERMINATED是主路径,没有BLOCKED/WAITING,因为调度由 JVM 用户态协程调度器接管
调试时别只看 getState(),要结合 jstack 和线程 dump
Thread.getState() 返回的是调用瞬间的快照,而线程状态切换极快。比如一个线程刚打印出 RUNNABLE,下一纳秒可能就因 I/O 进入 WAITING,但你的日志根本抓不到。真正定位问题得靠外部工具给出的全局视角。
立即学习“Java免费学习笔记(深入)”;
-
jstack <pid></pid>输出里,“java.lang.Thread.State: TIMED_WAITING (parking)” 表示它正被LockSupport.parkNanos()挂起,不是死锁 - 看到 “
java.lang.Thread.State: BLOCKED (on object monitor)” 且后面跟着 “waiting to lock0x0000000712345000”,说明它在等某个对象锁,去搜同个地址被谁持有(通常带 “locked”) - 如果线程 dump 里大量线程卡在
sun.misc.Unsafe.park(),大概率是ForkJoinPool或CompletableFuture内部调度问题,不是业务代码锁竞争 - 用
jcmd <pid> Thread.print</pid>比jstack更轻量,适合高频采样;但要注意它和目标JVM共享同一信号处理机制,频繁调用可能干扰应用本身
RUNNABLE 可能正在自旋,WAITING 可能等的是一个永远不会 notify 的对象,而 TERMINATED 的线程对象只要还被引用,就还在占用堆内存。








