状态转换全解
线程在六种状态之间是怎么跳来跳去的?
转换全景图
start()
┌────────────────────────────────────────────────────┐
│ │
▼ │
NEW ───▶ RUNNABLE ───────────────────────────────────▶ TERMINATED
│ │
│ run() 结束 / 抛异常 │
│ │
├────── synchronized 等待锁 ────▶ BLOCKED ──┤
│ (锁被占用) (获取到锁) │
│ │
├────── wait() / join() / park() ──▶ WAITING │
│ (无限期) (notify/unpark)│
│ │
└────── sleep() / wait(ms) / parkNanos() ──▶ TIMED_WAITING
(限时) (超时/被唤醒)每条转换路径详解
路径一:NEW → RUNNABLE
调用 start() 方法:
java
Thread thread = new Thread(() -> {});
System.out.println(thread.getState()); // NEW
thread.start();
System.out.println(thread.getState()); // RUNNABLE只能走一次,多次调用 start() 会抛 IllegalThreadStateException。
路径二:RUNNABLE → BLOCKED
等待获取 synchronized 锁:
java
Object lock = new Object();
Thread holder = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(5000); // 持有锁 5 秒
} catch (InterruptedException e) {}
}
});
Thread waiter = new Thread(() -> {
synchronized (lock) { // 等着拿锁
System.out.println("拿到锁了");
}
});
holder.start();
Thread.sleep(100); // 确保 holder 先拿锁
waiter.start();
System.out.println(waiter.getState()); // BLOCKED触发条件:其他线程持有了锁,自己想进但进不去。
离开方式:锁被释放后,JVM 会自动唤醒其中一个等待的线程。
路径三:RUNNABLE → WAITING
主动调用特定方法,无限期等待:
java
// 三种方式进入 WAITING:
// 方式1:wait()
synchronized (lock) {
lock.wait(); // 必须持有锁才能调用
}
// 方式2:join() 无参数
thread.join(); // 等 thread 结束
// 方式3:LockSupport.park()
LockSupport.park(); // 不需要持有锁WAITING 和 BLOCKED 的关键区别:
BLOCKED: 被动等锁,锁一释放就被唤醒(不用通知)
WAITING: 主动让出,必须有人 notify 才醒路径四:RUNNABLE → TIMED_WAITING
限时等待:
java
// 五种方式进入 TIMED_WAITING:
Thread.sleep(5000); // 方式1:sleep
synchronized (lock) {
lock.wait(5000); // 方式2:wait 带超时
}
thread.join(5000); // 方式3:join 带超时
LockSupport.parkNanos(5_000_000_000L); // 方式4:park 限秒
condition.await(5, TimeUnit.SECONDS); // 方式5:Condition 限秒TIMED_WAITING 和 WAITING 的区别:
WAITING: 无限期等,必须通知才醒
TIMED_WAITING: 限时等,超时自动醒唤醒路径
WAITING → BLOCKED → RUNNABLE
wait() 被 notify() 唤醒后,并不会直接进入 RUNNABLE,而是先进入 BLOCKED(因为还需要获取锁):
java
Thread waiting = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // RUNNABLE → WAITING
// 被 notify 后:WAITING → BLOCKED
// 获取到锁后:BLOCKED → RUNNABLE
} catch (InterruptedException e) {}
}
});
waiting.start();
Thread.sleep(100);
synchronized (lock) {
lock.notify(); // 唤醒 waiting
// waiting 此时是 BLOCKED(等锁),不是 RUNNABLE
}
// waiting 获取到锁,进入 RUNNABLE为什么 notify 后不是直接 RUNNABLE?
因为 wait() 是在 synchronized 块里调用的,wait 期间会释放锁。notify 只是告诉等待的线程「可以醒了」,但醒了之后还需要重新获取锁。
TIMED_WAITING → RUNNABLE
超时到期或被中断:
java
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000); // 等 3 秒
} catch (InterruptedException e) {}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // TIMED_WAITING
// 3 秒后...
thread.join();
System.out.println(thread.getState()); // TERMINATED完整示例:跟踪状态变化
java
public class StateTransitionDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
// 线程经历多种状态转换
Thread thread = new Thread(() -> {
// 1. RUNNABLE
System.out.println("1. " + Thread.currentThread().getState());
// 2. TIMED_WAITING(sleep)
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
// 3. WAITING(wait)
synchronized (lock) {
try {
System.out.println("3. 进入 wait");
lock.wait(); // 等待被唤醒
System.out.println("6. 被唤醒了");
} catch (InterruptedException e) {}
}
// 4. 继续运行,最终 TERMINATED
System.out.println("7. 任务完成");
}, "StateDemo");
thread.start();
Thread.sleep(100);
System.out.println("2. " + thread.getState()); // TIMED_WAITING
Thread.sleep(1100);
System.out.println("4. " + thread.getState()); // WAITING
Thread.sleep(100);
System.out.println("5. " + thread.getState()); // WAITING(还在等锁)
synchronized (lock) {
System.out.println("6. 持有锁,调用 notify");
lock.notify();
}
thread.join();
System.out.println("8. " + thread.getState()); // TERMINATED
}
}输出:
1. RUNNABLE
2. TIMED_WAITING
3. 进入 wait
4. WAITING
5. WAITING
6. 持有锁,调用 notify
6. 被唤醒了
7. 任务完成
8. TERMINATED状态转换速查表
| 当前状态 | 操作 | 目标状态 | 离开方式 |
|---|---|---|---|
| NEW | start() | RUNNABLE | - |
| RUNNABLE | run() 结束 | TERMINATED | - |
| RUNNABLE | 等锁 | BLOCKED | 锁释放 |
| RUNNABLE | wait() | WAITING | notify() / notifyAll() |
| RUNNABLE | join() | WAITING | 线程结束 / interrupt() |
| RUNNABLE | sleep(ms) | TIMED_WAITING | 超时 / interrupt() |
| RUNNABLE | park() | WAITING | unpark() / interrupt() |
| BLOCKED | 获取到锁 | RUNNABLE | - |
| WAITING | notify() | BLOCKED | 获取到锁 |
| WAITING | unpark() | RUNNABLE | - |
| TIMED_WAITING | 超时 | RUNNABLE | - |
| TIMED_WAITING | 被唤醒 | BLOCKED | 获取到锁 |
实战:用 jstack 分析状态
线上出问题?用 jstack 看线程状态:
bash
# 查看 Java 进程的线程状态
jstack <pid>
# 查找处于 BLOCKED 状态的线程
jstack <pid> | grep -A 10 "java.lang.Thread.State: BLOCKED"
# 查找处于 WAITING 状态的线程
jstack <pid> | grep -A 10 "java.lang.Thread.State: WAITING"jstack 常见状态解读:
# BLOCKED - 等锁
"pool-1-thread-2" prio=5 tid=0x... nid=0x... waiting for monitor entry
java.lang.Thread.State: BLOCKED
# WAITING - 等通知
"pool-1-thread-1" prio=5 tid=0x... nid=0x... in Object.wait()
java.lang.Thread.State: WAITING
# TIMED_WAITING - 等超时
"pool-1-thread-3" prio=5 tid=0x... nid=0x... waiting on condition
java.lang.Thread.State: TIMED_WAITING总结
- 线程状态转换是单向的:NEW → RUNNABLE → TERMINATED
- BLOCKED 是被动等锁,WAITING 是主动等待
- TIMED_WAITING 是带超时的等待
wait()被notify()唤醒后,先进入 BLOCKED(需要重新获取锁)- 理解状态转换是分析死锁、性能问题的前提
