线程有几种状态,状态之间是怎样流转的?
2026年01月11日
一则或许对你有用的小广告
欢迎 加入小哈的星球 ,你将获得: 专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目: 《Spring AI 项目实战(问答机器人、RAG 增强检索、联网搜索)》 正在持续爆肝中,基于
Spring AI + Spring Boot3.x + JDK 21..., 点击查看; - 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot3.x + JDK 17..., 点击查看项目介绍; 演示链接: http://116.62.199.48:7070/; - 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/
面试考察点
当面试官询问线程状态时,其核心考察点远不止于记忆几个状态名称。他们通常希望了解:
- 对 Java 并发模型基础概念的掌握程度:这是并发编程的基石。
- 对线程生命周期和状态转换触发条件的深入理解:面试官不仅仅想知道 “有哪几种状态”,更想知道 “在什么情况下,线程会从A状态变成B状态”。
- 将理论联系实际调试和开发的能力:是否能在程序卡死、性能低下时,通过线程状态(如
jstack命令输出)快速定位问题(如死锁、线程饥饿、无限等待)。 - 对底层机制(JVM 与操作系统调度)的认知:理解
RUNNABLE状态与操作系统线程状态的映射关系。
核心答案
在 Java 中,线程的状态在 java.lang.Thread.State 枚举中明确定义,共有 6 种:
- NEW:新建状态,线程被创建但尚未启动。
- RUNNABLE:可运行状态,包括了操作系统线程状态中的 就绪(Ready) 和 运行中(Running)。线程可能在等待 CPU 时间片,也可能正在执行。
- BLOCKED:阻塞状态,线程在等待进入一个由
synchronized关键字保护的同步块或方法时陷入的状态,其前提是所需的锁正被其他线程持有。 - WAITING:无限期等待状态,线程进入此状态后,需要等待其他线程显式地唤醒。例如,调用了
Object.wait()、Thread.join()或LockSupport.park()方法。 - TIMED_WAITING:超时等待状态,与
WAITING类似,但设置了最长等待时间。例如,调用了Thread.sleep(long millis)、Object.wait(long timeout)、Thread.join(long millis)等方法。 - TERMINATED:终止状态,表示线程已经执行完毕。
状态的流转是一个闭环:线程由 NEW 开始,通过 start() 进入 RUNNABLE。在 RUNNABLE 中,可能因等待锁、主动放弃 CPU 或等待条件而进入 BLOCKED、WAITING 或 TIMED_WAITING。当等待的条件满足或被中断后,线程会重新回到 RUNNABLE 状态等待调度。最终,线程执行完毕或发生未捕获异常而进入 TERMINATED 状态。
深度解析
原理与机制
RUNNABLE的广义性:这是 Java 语言层面的状态,它对应了操作系统线程状态中的 “就绪” 和 “运行”。因此,一个处于RUNNABLE状态的 Java 线程,在操作系统看来可能正在运行,也可能在就绪队列中等待 CPU。这是理解 Java 线程调度的关键。BLOCKED与WAITING的本质区别:这是最常见的混淆点。BLOCKED是 “主动的、竞争的”。线程在积极尝试获取一个已经锁定的synchronized监视器锁。一旦锁被释放,所有在该锁上BLOCKED的线程会去竞争,只有一个能成功。WAITING/TIMED_WAITING是 “被动的、协作的”。线程通常是主动释放了锁(如调用wait()会释放锁),然后进入等待队列,直到被其他线程通过notify()/notifyAll()或条件满足(超时、中断)唤醒。被唤醒后,线程需要重新去获取锁(这可能会再次进入BLOCKED状态),才能恢复到RUNNABLE。
- 流转触发点:每个状态变化都由特定的方法调用或系统事件触发,理解这些 “扳机” 是分析并发问题的核心。
代码示例与状态流转:
public class ThreadStateDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread thread = new Thread(() -> {
synchronized (lock) {
try {
// 从 RUNNABLE 进入 TIMED_WAITING
Thread.sleep(1000);
// 从 RUNNABLE 进入 WAITING,并释放 lock
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 状态 1: NEW
System.out.println("1. After creation: " + thread.getState());
thread.start();
Thread.sleep(10); // 确保主线程稍后执行,让子线程有机会运行
// 状态 2: RUNNABLE (子线程已启动,可能在执行或等待CPU)
System.out.println("2. After start: " + thread.getState());
Thread.sleep(100);
// 状态 3: TIMED_WAITING (因为 sleep)
System.out.println("3. During sleep: " + thread.getState());
Thread.sleep(1000); // 等待 sleep 结束
// 状态 4: WAITING (因为 wait)
System.out.println("4. During wait: " + thread.getState());
synchronized (lock) {
lock.notify(); // 唤醒子线程
Thread.sleep(100);
// 状态 5: BLOCKED (子线程被唤醒,但需要重新获取 lock,而 lock 被主线程持有)
System.out.println("5. After notify, trying to re-acquire lock: " + thread.getState());
}
Thread.sleep(100);
// 状态 6: TERMINATED (子线程执行完毕)
System.out.println("6. After all: " + thread.getState());
}
}
常见误区与最佳实践
- 误区1:
RUNNABLE就等于“正在运行”。不对,它只代表 Java 线程 “有资格运行”,可能正在运行,也可能在等待系统资源。 - 误区2:线程休眠(
sleep)时持有锁。是的,sleep不会释放任何监视器锁(如synchronized或Lock对象),这与wait()不同。 - 最佳实践:在生产环境排查性能问题时,应熟练使用
jstack、jconsole或可视化工具(如 Arthas)来捕获线程转储(Thread Dump),并准确识别出BLOCKED、WAITING状态的线程及其堆栈,这能快速定位锁竞争、死锁或资源等待问题。
总结
Java 线程的 6 种状态及其流转机制是并发编程的底层核心,深刻理解 RUNNABLE 的广义性以及 BLOCKED 和 WAITING 的本质区别,是将并发理论应用于实际问题诊断和解决的关键。