面试考察点
基础掌握度:面试官不仅仅是想知道有几种状态,更是想考察你是否能准确说出 Java 层面(Thread.State)的 6 种状态,而不是和操作系统的 5 种状态混为一谈。
状态流转理解:能否完整描述每种状态之间的转换条件,特别是 BLOCKED、WAITING、TIMED_WAITING 这三种 "停顿" 状态的区别和触发场景,这是面试的重点。
实践关联:能否结合 synchronized、Lock、sleep()、wait() 等实际 API 来解释状态流转,而不是干巴巴背概念。
核心答案
Java 线程在 Thread.State 枚举中定义了 6 种状态:
状态
含义
是否占用 CPU
NEW
新建,还没调用 start()
否
RUNNABLE
可运行(包括就绪和运行中)
可能
BLOCKED
阻塞,等待获取锁
否
WAITING
等待,无限期等待被唤醒
否
TIMED_WAITING
超时等待,有期限的等待
否
TERMINATED
终止,线程执行完毕
否
一句话总结:6 种状态,核心流转路径是 NEW → RUNNABLE →(BLOCKED/WAITING/TIMED_WAITING)→ RUNNABLE → TERMINATED。
深度解析
一、完整状态流转图
图片来源:https://mp.weixin.qq.com/s/0UTyrJpRKaKhkhHcQtXAiA
上图展示了 Java 线程 6 种状态之间的完整流转关系。下面逐条解释每条转换路径:
NEW → RUNNABLE:调用 start() 方法,线程从新建状态进入可运行状态,交给操作系统调度
RUNNABLE → BLOCKED:线程尝试进入 synchronized 代码块/方法,但锁被其他线程持有,进入阻塞状态
RUNNABLE → WAITING:线程调用 Object.wait()、Thread.join()、LockSupport.park() 等方法,主动进入无限期等待
RUNNABLE → TIMED_WAITING:线程调用 Thread.sleep(n)、Object.wait(n)、Thread.join(n) 等带超时的方法
BLOCKED → RUNNABLE:锁释放后,该线程竞争成功获取到锁
WAITING → RUNNABLE:被 notify()/notifyAll() 唤醒,或 LockSupport.unpark() 唤醒,或等待的线程执行完毕(join() 场景)
TIMED_WAITING → RUNNABLE:超时时间到自动唤醒,或在超时前被显式唤醒
WAITING/TIMED_WAITING → BLOCKED:被 notify() 唤醒后需要重新获取锁,如果锁被其他线程持有则进入 BLOCKED
RUNNABLE → TERMINATED:run() 方法执行完毕,或抛出未捕获的异常
二、6 种状态逐一详解
1. NEW(新建)
线程对象已创建,但还没调用 start() 方法。此时线程还没分配系统资源。
Thread t = new Thread(() -> {}); // 此时状态:NEW
// t.start(); // 调用 start() 后才变为 RUNNABLE
2. RUNNABLE(可运行)
注意,Java 层面的 RUNNABLE 包含了操作系统层面的 就绪(Ready) 和 运行中(Running) 两个状态。Java 不区分这两个,统一算 RUNNABLE。
就绪:线程已经准备好,等 CPU 分配时间片
运行中:CPU 正在执行该线程
Thread t = new Thread(() -> {
// 这里面执行时,状态就是 RUNNABLE
// 即使调用了 IO 操作,Java 层面状态仍然是 RUNNABLE(这是 Java 的设计选择)
});
t.start(); // NEW → RUNNABLE
3. BLOCKED(阻塞)
线程在等待获取一把 内置锁(synchronized)。当锁被其他线程持有时,尝试进入 synchronized 代码块/方法的线程就会变成 BLOCKED。
synchronized (lock) { // 如果 lock 被其他线程持有
// 线程状态:BLOCKED // 等待获取锁
// 获取到锁后 → RUNNABLE
}
注意:BLOCKED 只针对 synchronized,使用 ReentrantLock.lock() 时线程进入的是 WAITING(因为底层调用了 LockSupport.park()),不是 BLOCKED。
4. WAITING(无限期等待)
线程进入等待状态,需要其他线程显式唤醒。以下操作会让线程进入 WAITING:
方法
唤醒条件
Object.wait()
需要 notify()/notifyAll()
Thread.join()
等待目标线程执行完毕
LockSupport.park()
需要 LockSupport.unpark()
// 示例:wait/notify
synchronized (lock) {
lock.wait(); // RUNNABLE → WAITING
// 等其他线程调用 lock.notify()
// 被唤醒后 → BLOCKED(重新抢锁)
// 抢到锁后 → RUNNABLE
}
5. TIMED_WAITING(超时等待)
和 WAITING 类似,区别是 有超时时间,时间到了自动唤醒。以下操作会让线程进入 TIMED_WAITING:
方法
说明
Thread.sleep(long)
休眠指定时间
Object.wait(long)
超时等待
Thread.join(long)
超时等待目标线程
LockSupport.parkNanos()
超时阻塞
LockSupport.parkUntil()
阻塞到指定时间点
Thread.sleep(1000); // RUNNABLE → TIMED_WAITING
// 1 秒后自动 → RUNNABLE
6. TERMINATED(终止)
线程执行完毕或因异常退出,不可再启动。
Thread t = new Thread(() -> {});
t.start();
t.join(); // 等待线程执行完
// 此时状态:TERMINATED
三、三种 "停顿" 状态的区别(面试重点)
BLOCKED、WAITING、TIMED_WAITING 都表示线程 "没在跑",但本质不同:
对比维度
BLOCKED
WAITING
TIMED_WAITING
触发原因
等待 synchronized 锁
wait()/join()/park()
sleep()/wait(n)/parkNanos()
等待时长
不确定(等锁释放)
不确定(等别人唤醒)
确定的超时时间
唤醒方式
锁释放后自动竞争
需要显式 notify()/unpark()
超时自动唤醒,或被显式唤醒
是否释放锁
还没获取到锁
wait() 会释放锁
wait(n) 会释放锁
jstack 中常见
waiting for monitor entry
waiting on condition / Object.wait()
sleeping / parking to wait
常见误区澄清:
Thread.sleep() 不会释放锁,但 Object.wait() 会释放锁
synchronized 获取锁失败 → BLOCKED;ReentrantLock.lock() 等待 → WAITING(因为底层用 LockSupport.park())
Java 的 IO 阻塞(如 Socket.read())在 Java 层面状态仍然是 RUNNABLE,不会变成 BLOCKED 或 WAITING,这是因为 Java 把操作系统层面的 IO 阻塞也归入了 RUNNABLE
四、常见 API 对应的状态流转速查
API 调用
状态变化
恢复条件
t.start()
NEW → RUNNABLE
-
Thread.sleep(n)
RUNNABLE → TIMED_WAITING
超时自动恢复
Object.wait()
RUNNABLE → WAITING
notify()/notifyAll()
Object.wait(n)
RUNNABLE → TIMED_WAITING
超时 或 notify()
Thread.join()
RUNNABLE → WAITING
目标线程执行完毕
Thread.join(n)
RUNNABLE → TIMED_WAITING
超时 或目标线程执行完
LockSupport.park()
RUNNABLE → WAITING
unpark() 或中断
LockSupport.parkNanos()
RUNNABLE → TIMED_WAITING
超时 或 unpark()
进入 synchronized(锁被占)
RUNNABLE → BLOCKED
锁释放后竞争成功
notify() 唤醒后
WAITING → BLOCKED
获取到锁后 → RUNNABLE
run() 执行完
RUNNABLE → TERMINATED
不可逆
面试高频追问
Java 线程状态和操作系统线程状态有什么区别?
操作系统层面线程有 5 种状态(创建、就绪、运行、阻塞、终止),Java 的 RUNNABLE 对应了 OS 的 "就绪 + 运行",Java 的 BLOCKED、WAITING、TIMED_WAITING 都对应 OS 的 "阻塞"。另外 Java 把 BIO 的阻塞也归入 RUNNABLE,而 OS 层面是 "阻塞"。
BLOCKED 和 WAITING 有什么区别?
BLOCKED 是等待获取 synchronized 锁(被动阻塞),WAITING 是主动调用 wait()/join()/park() 进入等待(主动等待)。BLOCKED 不需要别人唤醒,锁一释放就自动去抢;WAITING 必须等别人显式唤醒。
sleep() 和 wait() 的状态变化有什么不同?
sleep(n) 让线程进入 TIMED_WAITING,不释放锁;wait() 让线程进入 WAITING,会释放锁。sleep() 到时间自动恢复,wait() 需要别人 notify()。
常见面试变体
"说说 Thread.sleep() 和 Object.wait() 的区别?"
"BLOCKED 和 WAITING 有什么区别?"
"如何用 jstack 排查线程阻塞问题?"
"为什么 Java 没有区分就绪和运行两个状态?"
记忆口诀
6 种状态:新建、可运行、阻塞、等待、超时等待、终止
流转主线:NEW(出生)→ RUNNABLE(上班)→ 三种摸鱼姿势(BLOCKED 等锁、WAITING 等通知、TIMED_WAITING 等闹钟)→ RUNNABLE(继续干活)→ TERMINATED(下班)
三种等待区分:BLOCKED 被锁挡、WAITING 等人叫、TIMED_WAITING 等闹钟
总结
Java 线程有 6 种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。核心流转是创建后 start() 进入 RUNNABLE,运行过程中可能因锁竞争进入 BLOCKED,因 wait()/join()/park() 进入 WAITING,因 sleep()/wait(n) 进入 TIMED_WAITING,最终执行完毕进入 TERMINATED。重点区分三种 "停顿" 状态的触发条件和恢复方式。