线程的生命周期
- New:新建
- Runnable:就绪
- Running:运行
- Blocked:阻塞
- Dead:死亡
五状态模型是操作系统教材的视角;Java 层面真正用于排障的是 Thread.State 枚举的六状态。两套模型的映射关系、以及每个阻塞状态对应的排查手段,才是这个知识点的实战价值。
Java 的六状态与五状态映射
| Thread.State | 含义 | 对应五状态 |
|---|---|---|
| NEW | 已 new 未 start | New |
| RUNNABLE | 可运行/运行中(含等 CPU 与等 IO 的”假运行”) | Runnable + Running |
| BLOCKED | 等监视器锁(synchronized 竞争失败) | Blocked |
| WAITING | 无限期等待他线程动作(wait/join/park) | Blocked |
| TIMED_WAITING | 带超时的等待(sleep(t)/wait(t)/parkNanos) | Blocked |
| TERMINATED | 执行结束或异常退出 | Dead |
关键差异:Java 把”就绪”与”运行”合并为 RUNNABLE——因为 JVM 无法区分线程是在跑还是在等 CPU 调度(那是 OS 的事)。更反直觉的是:Linux 上阻塞于磁盘 IO 的线程在 JVM 里仍显示 RUNNABLE(内核态等待不计入 Java 状态),所以”RUNNABLE 但接口慢”往往要往 IO 方向查。
状态迁移与触发 API
- NEW → RUNNABLE:
start()(注意:重复 start 抛 IllegalThreadStateException) - RUNNABLE → BLOCKED:进入 synchronized 块/方法竞争锁失败
- RUNNABLE → WAITING:
Object.wait()、Thread.join()、LockSupport.park() - RUNNABLE → TIMED_WAITING:
Thread.sleep(t)、wait(t)、join(t)、parkNanos - WAITING/BLOCKED → RUNNABLE:被 notify/中断/获得锁/目标线程结束
- 任意 → TERMINATED:run() 正常返回或异常抛出
排障视角:线程 dump 里的状态读法
jstack 输出中每个线程带状态标注,对应典型问题:
- 大量 BLOCKED 在同一地址 → 锁竞争热点(找持锁线程在干什么:长事务?IO 在锁内?)
- 大量 WAITING 在
getTask/await→ 线程池空闲或队列空,正常;若业务线程 WAITING 在 join/park 不醒 → 协作方死了,查配对生命周期 - TIMED_WAITING 在 sleep → 代码里有显式 sleep(常见于轮询等待,考虑改条件变量)
- 全部 RUNNABLE 但 CPU 100% → 死循环或自旋(CAS 重试风暴)
- RUNNABLE 但 CPU 低、接口慢 → 内核态 IO 等待(数据库/网络),Java 状态看不见,需配合 netstat/慢查询日志
死亡与复活
线程 TERMINATED 后不可重启(再次 start 抛异常);线程池里”线程因任务异常死亡”由池补充新 Worker(线程对象是新的,不是复活)。理解这一点能解释”线程池里的线程换了但池还在”的现象。
中断:状态之外的协作信号
中断(interrupt)不直接改变状态,而是设置标志位:阻塞在 wait/sleep/join 的线程收到中断抛 InterruptedException 并清标志;RUNNABLE 线程需自行检查 isInterrupted() 响应。优雅停止线程的标准姿势是”中断 + 循环检查”,而非已废弃的 stop()(强制释放锁会破坏不变量)。
五状态是概念模型,六状态是排障工具:BLOCKED 指向锁、WAITING 指向协作、TIMED_WAITING 指向定时、RUNNABLE 的两种隐藏形态(等 CPU/等 IO)指向性能分析。把状态与排查动作绑定记忆,生命周期就不再是背诵题。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 非鱼小站!
评论
WalineDisqus











