深入 JUC 入门核心:Java 线程状态全解析——从 NEW 到 TERMINATED 的完整生命周期(Java 实习生必修课)
适用人群
- 计算机科学与技术、软件工程等专业的在校本科生或研究生,正在学习《操作系统》《并发编程》等课程;
- Java 初级开发者或实习生,希望系统掌握线程状态模型及其在并发编程中的应用;
- 准备 Java 后端岗位面试,需深入理解
Thread.State枚举、状态转换条件及调试技巧; - 对 JVM 线程调度、线程阻塞/唤醒机制、死锁排查等底层原理感兴趣的开发者。
本文假设读者已掌握 Java 基础语法,并对“线程创建”“常用方法(如 sleep、join、interrupt)”有初步了解。内容将从JVM 规范 → 操作系统映射 → Java API 层三层递进,全面剖析 Java 线程的六种状态、转换条件、监控手段及实战案例,助你构建清晰的并发执行模型。
关键词
JUC、Java 并发、多线程、线程状态、Thread.State、NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED、线程生命周期、jstack、线程转储、死锁检测、Java 实习生、计算机专业核心课、JUC 入门、并发编程基础、操作系统线程模型。
引言:为什么“线程状态”是并发调试的罗盘?
在单线程程序中,代码执行路径清晰可见。但一旦引入多线程,程序行为变得非确定性——某个线程可能卡住、某个任务迟迟不返回、系统 CPU 飙升却无业务进展……
此时,若你只会说“线程好像卡了”,而无法精准定位其当前处于何种状态、为何卡住、如何恢复,那么你将难以胜任任何涉及并发的开发或运维工作。
Java 通过Thread.State枚举明确定义了线程的六种标准状态,这不仅是理论模型,更是线上问题诊断的核心依据。无论是使用jstack查看线程堆栈,还是分析 APM 监控中的线程池指标,背后都依赖于对线程状态的准确理解。
本文将带你:
- 逐个剖析六种线程状态(NEW ~ TERMINATED)的含义与触发条件;
- 绘制完整状态转换图,揭示
sleep()、wait()、synchronized、join()等操作如何驱动状态变迁; - 演示真实监控工具(jstack、VisualVM)如何反映线程状态;
- 分析典型问题场景(死锁、活锁、无限等待)中的状态特征;
- 提供调试与优化建议。
全文超过 9000 字,包含大量图解、源码片段、命令行操作与面试高频问题,助你彻底掌握线程状态这一并发基石。
一、Java 线程状态模型:六种标准状态
自 JDK 5 起,Java 在java.lang.Thread类中定义了State枚举,明确规范了线程在其生命周期中可能处于的六种状态:
publicenumState{NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}⚠️重要前提:
这些状态是JVM 层面的抽象,并不完全等同于操作系统的线程状态(如 Linux 的 TASK_RUNNING、TASK_INTERRUPTIBLE 等),但存在映射关系。
下表为六种状态的概览:
| 状态 | 中文名 | 说明 | 是否消耗 CPU |
|---|---|---|---|
| NEW | 新建 | 线程对象已创建,但未调用start() | ❌ |
| RUNNABLE | 可运行 | 已调用start(),等待或正在 CPU 上执行 | ✅(若正在运行) |
| BLOCKED | 阻塞 | 等待获取synchronized锁 | ❌ |
| WAITING | 等待 | 无限期等待其他线程显式唤醒 | ❌ |
| TIMED_WAITING | 超时等待 | 有时间限制的等待 | ❌ |
| TERMINATED | 终止 | 线程执行完毕或异常退出 | ❌ |
接下来,我们将逐一详解每种状态。
二、状态详解与代码示例
2.1 NEW:新建状态
定义:线程对象已通过new Thread()创建,但尚未调用start()方法。
Threadt=newThread(()->System.out.println("Hello"));System.out.println(t.getState());// 输出: NEW特征:
- 此时线程尚未与 OS 内核线程关联;
- 未分配独立的 Java 栈;
- 无法被调度执行。
✅注意:直接调用
t.run()不会改变状态,仍为NEW,因为未启动新线程。
2.2 RUNNABLE:可运行状态
定义:线程已调用start(),处于就绪(Ready)或运行(Running)状态。JVM 将其统称为RUNNABLE。
Threadt=newThread(()->{while(true){// 空循环,持续占用 CPU}});t.start();// 短暂延迟后查看状态Thread.sleep(10);System.out.println(t.getState());// 很可能输出: RUNNABLE关键点:
- 包含两种 OS 状态:
- 就绪(Ready):已准备好,等待 CPU 时间片;
- 运行(Running):正在 CPU 上执行。
- JVM不区分这两种子状态,统一为
RUNNABLE; - 此状态可能消耗 CPU(若正在运行)。
🔍调试技巧:
若发现大量线程处于RUNNABLE且 CPU 使用率高,可能是忙等待(busy-wait)或计算密集型任务。
2.3 BLOCKED:阻塞状态
定义:线程试图进入synchronized同步块/方法,但所需对象的监视器锁(Monitor Lock)已被其他线程持有。
Objectlock=newObject();Threadt1=newThread(()->{synchronized(lock){try{Thread.sleep(5000);}// 持有锁 5 秒catch(InterruptedExceptione){}}});Threadt2=newThread(()->{synchronized(lock){System.out.println("t2 acquired lock");}});t1.start();Thread.sleep(100);// 确保 t1 先拿到锁t2.start();// 查看 t2 状态Thread.sleep(100);System.out.println("t2 state: "+t2.getState());// 输出: BLOCKED特征:
- 仅由
synchronized引起;ReentrantLock等显式锁不会使线程进入BLOCKED(而是WAITING); - 不消耗 CPU;
- 一旦持有锁的线程释放锁,
BLOCKED线程将竞争锁,成功者进入RUNNABLE。
⚠️死锁标志:
若多个线程相互BLOCKED,形成环路,则发生死锁(Deadlock)。
2.4 WAITING:无限等待状态
定义:线程调用以下方法之一,进入无限期等待,直到被其他线程显式唤醒:
Object.wait()Thread.join()LockSupport.park()
Objectobj=newObject();Threadwaiter=newThread(()->{synchronized(obj){try{obj.wait();// 无限等待}catch(InterruptedExceptione){e.printStackTrace();}}});waiter.start();Thread.sleep(100);System.out.println("Waiter state: "+waiter.getState());// 输出: WAITING特征:
- 必须被显式唤醒:
notify()/notifyAll()(针对wait())、目标线程结束(针对join())、unpark()(针对park()); - 不消耗 CPU;
- 释放对象监视器锁(仅
wait()会释放,join()和park()不涉及锁)。
📌注意:
WAITING与BLOCKED的区别:
BLOCKED是抢锁失败;WAITING是主动放弃执行权,等待通知。
2.5 TIMED_WAITING:超时等待状态
定义:线程调用带超时参数的方法,进入有时间限制的等待:
Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos()LockSupport.parkUntil()
Threadsleeper=newThread(()->{try{Thread.sleep(3000);// 休眠 3 秒}catch(InterruptedExceptione){e.printStackTrace();}});sleeper.start();Thread.sleep(100);System.out.println("Sleeper state: "+sleeper.getState());// 输出: TIMED_WAITING特征:
- 自动唤醒:超时后自动恢复为
RUNNABLE; - 也可被中断唤醒(抛出
InterruptedException); - 不消耗 CPU。
✅最佳实践:
优先使用带超时的方法(如wait(5000)而非wait()),避免永久挂起。
2.6 TERMINATED:终止状态
定义:线程的run()方法正常执行完毕,或因未捕获异常而退出。
Threadt=newThread(()->{System.out.println("Task done.");});t.start();t.join();// 等待结束System.out.println("Final state: "+t.getState());// 输出: TERMINATED特征:
- 线程不可复用;
- 所有资源(如栈)被回收;
- 无法再被启动。
⚠️注意:线程对象仍存在于堆中(可被 GC),但其代表的执行实体已消亡。
三、线程状态转换全景图
下图展示了六种状态之间的合法转换路径及触发条件:
+--------+ | NEW | +---+----+ | start() v +------+-------+ | RUNNABLE |<------------------+ +------+-------+ | | | +-------v-------+ +-------------v-------------+ | BLOCKED | | WAITING / | | (抢 synchronized | | TIMED_WAITING | | 锁失败) | | (wait/sleep/join/park) | +-------+-------+ +-------------+-------------+ | | acquire lock notify()/interrupt()/timeout | | +------------->-------------+ | run() ends 或 异常退出 v +-------+--------+ | TERMINATED | +----------------+ 【补充路径】 - RUNNABLE → TIMED_WAITING: sleep(n), wait(n), join(n) - RUNNABLE → WAITING: wait(), join(), park() - RUNNABLE → BLOCKED: 尝试进入 synchronized 块但锁被占 - WAITING/TIMED_WAITING → RUNNABLE: 被 notify/unpark/interrupt/超时 - BLOCKED → RUNNABLE: 成功获取 synchronized 锁🔑核心驱动因素:
- 同步原语:
synchronized、wait/notify;- 线程方法:
sleep()、join()、interrupt();- 锁工具:
LockSupport.park()。
四、如何监控线程状态?实战工具演示
4.1 使用Thread.getState()(开发阶段)
Threadt=newThread(()->{/* ... */});System.out.println(t.getState());// NEWt.start();// ...System.out.println(t.getState());// 可能为 RUNNABLE/WAITING 等⚠️局限性:仅适用于可访问
Thread对象的场景,无法用于线上诊断。
4.2 使用jstack(生产环境首选)
jstack是 JDK 自带的线程转储工具,可打印 JVM 中所有线程的堆栈及状态。
操作步骤:
获取 Java 进程 ID:
jps# 输出: 12345 MyApplication生成线程转储:
jstack12345>thread_dump.txt
输出片段解析:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f8b8c00a000 nid=0x3e8 waiting on condition [0x00007f8b7d0fe000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.example.SleepDemo.lambda$main$0(SleepDemo.java:8) ... "Thread-2" #13 prio=5 os_prio=0 tid=0x00007f8b8c00b000 nid=0x3e9 waiting for monitor entry [0x00007f8b7d1ff000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.BlockDemo.lambda$main$1(BlockDemo.java:15) - waiting to lock <0x000000076b800000> (a java.lang.Object) ...关键信息:
java.lang.Thread.State: 显示 Java 线程状态;waiting on condition: 通常对应WAITING/TIMED_WAITING;waiting for monitor entry: 对应BLOCKED;nid: Native Thread ID,可用于top -H定位高 CPU 线程。
✅死锁检测:
jstack会自动检测死锁,并在输出末尾提示:Found one Java-level deadlock: ============================= "Thread-1": waiting to lock monitor 0x00007f8b8c00a000 (object 0x000000076b800000) "Thread-2": waiting to lock monitor 0x00007f8b8c00b000 (object 0x000000076b800010)
4.3 使用 VisualVM / JConsole(图形化)
- 启动 VisualVM;
- 连接目标进程;
- 切换到 “Threads” 标签页;
- 查看每个线程的名称、状态、堆栈;
- 点击 “Thread Dump” 按钮生成快照。
🖥️优势:直观、支持历史对比、可监控 CPU 时间。
4.4 使用 Arthas(阿里开源,线上无侵入)
# 查看所有线程状态thread# 查看特定线程堆栈thread<tid># 查找最忙的线程thread -n3✅适合生产环境:无需重启、低开销。
五、典型问题场景中的线程状态分析
5.1 场景一:死锁(Deadlock)
现象:系统无响应,CPU 正常,部分请求卡住。
线程状态特征:
- 多个线程处于
BLOCKED; - 形成循环等待链;
jstack明确报告 “Found one Java-level deadlock”。
解决思路:
- 按相同顺序获取锁;
- 使用
tryLock(timeout)避免无限等待; - 重构代码,减少锁粒度。
5.2 场景二:无限等待(Infinite Wait)
现象:线程数稳定增长,内存缓慢上升,任务不完成。
线程状态特征:
- 大量线程处于
WAITING; - 调用栈显示
Object.wait()或Thread.join(); - 无对应的
notify()或目标线程永不结束。
常见原因:
- 忘记调用
notify(); - 生产者-消费者模型中,生产者异常退出;
join()等待一个永远不会结束的线程。
解决思路:
- 始终使用带超时的等待;
- 添加健康检查与超时熔断。
5.3 场景三:高 CPU + 大量 RUNNABLE 线程
现象:CPU 使用率 100%,系统响应慢。
线程状态特征:
- 多个线程处于
RUNNABLE; - 堆栈显示空循环或正则表达式回溯。
解决思路:
- 检查是否有
while (true) {}未加sleep(); - 使用
jstack定位热点代码; - 优化算法复杂度。
六、线程状态与 JUC 工具类的关系
虽然Thread.State主要描述synchronized和Thread方法的行为,但 JUC 工具类也会影响线程状态:
| JUC 工具 | 底层机制 | 线程状态 |
|---|---|---|
ReentrantLock.lock() | LockSupport.park() | →WAITING |
CountDownLatch.await() | LockSupport.park() | →WAITING/TIMED_WAITING |
Semaphore.acquire() | LockSupport.park() | →WAITING |
BlockingQueue.take() | LockSupport.park() | →WAITING |
📌关键区别:
synchronized→BLOCKED;- JUC 锁/队列 →
WAITING(因为基于park/unpark)。
七、学习建议与扩展阅读
7.1 动手实验清单
- 状态转换验证:编写程序,依次触发六种状态并打印;
- 死锁模拟:两个线程交叉获取两把锁,用
jstack检测; - WAITING vs BLOCKED:分别用
synchronized和ReentrantLock实现竞争,观察状态差异; - Arthas 实战:在 demo 服务中使用
thread命令查看状态。
7.2 推荐资料
- 📘《Java 并发编程实战》(Brian Goetz)
第 5 章“基础构建模块”、第 10 章“避免活跃性危险”。 - 📘《深入理解 Java 虚拟机》— 周志明
第 12 章“Java 内存模型与线程”。 - 📄Oracle Thread State Documentation
官方 API 文档。 - 🎥Bilibili 视频:
- 尚硅谷《JUC 并发编程》
- 美团技术团队《Java 线程状态与死锁排查》
7.3 面试高频问题
- Java 线程有哪些状态?如何转换?
BLOCKED和WAITING的区别是什么?- 如何用
jstack分析死锁? - 为什么
ReentrantLock不会导致BLOCKED状态? RUNNABLE状态是否一定在消耗 CPU?
八、总结
线程状态是理解 Java 并发行为的“地图”。本文系统讲解了:
- 六种标准状态:从
NEW到TERMINATED的完整生命周期; - 状态转换机制:由同步原语、线程方法驱动的状态变迁;
- 监控与诊断:
jstack、VisualVM、Arthas 的实战使用; - 问题场景分析:死锁、无限等待、高 CPU 的状态特征;
- JUC 工具影响:显式锁与
synchronized的状态差异。
最后寄语:
优秀的并发程序员,不仅能写出正确的多线程代码,
更能在系统“生病”时,通过线程状态快速定位病灶。
从今天起,把jstack当作你的“听诊器”,
用状态模型指导你的并发设计,
你将成为团队中不可或缺的稳定性守护者。
欢迎在评论区交流!
👉 你在实习中是否通过线程状态成功排查过线上问题?
👉 对哪种状态的转换机制最感兴趣?
点赞 + 收藏 + 关注,获取更多 JUC 与并发编程干货!🚀