线程六种状态
理解状态,才能读懂线程的一生。
先看一个问题
当你用 jstack 查看线上进程时,看到一堆线程状态:
"pool-1-thread-3" #42 prio=5 os_prio=31 tid=0x00007f8a5400a000 nid=0x5a03 waiting for monitor entry [0x0000700012e3c000]
java.lang.Thread.State: BLOCKED
"pool-1-thread-2" #40 prio=5 os_prio=31 tid=0x00007f8a54009000 nid=0x5603 waiting on condition [0x0000700012d39000]
java.lang.Thread.State: TIMED_WAITING
"pool-1-thread-1" #38 prio=5 os_prio=31 tid=0x00007f8a54007000 nid=0x5203 in Object.wait() [0x0000700012c36000]
java.lang.Thread.State: WAITING这些 BLOCKED、TIMED_WAITING、WAITING 到底代表什么?线程为什么会进入这些状态?
要回答这些问题,先得搞清楚 Java 线程的六种状态。
六种状态全景图
java
public enum State {
NEW, // 新建
RUNNABLE, // 可运行(包含就绪和运行中)
BLOCKED, // 阻塞(等待锁)
WAITING, // 等待(无限期)
TIMED_WAITING,// 等待(限时)
TERMINATED // 终止
} ┌─────────────────────────────────────────┐
│ 线程生命周期 │
└─────────────────────────────────────────┘
NEW ────────────▶ RUNNABLE ───────────────────────▶ TERMINATED
start() run() 正常返回 / 抛异常
RUNNABLE ──────────────────────────────────────────────────────────┐
│ │
├──── synchronized 等待锁 ────▶ BLOCKED ◀─── 获取到锁 ───────────┤
│ │
├──── wait() / join() / park() ──▶ WAITING ◀─── notify / unpark ─┤
│ │
└──── sleep(ms) / wait(ms) / parkNanos() ──▶ TIMED_WAITING ◀─────┘
超时 / 被唤醒NEW:新建状态
线程对象创建了,但还没跟操作系统打过招呼:
java
Thread thread = new Thread(() -> {
System.out.println("hello");
});
System.out.println(thread.getState()); // NEW
// 此时只是一个普通 Java 对象,尚未绑定 OS 线程特点:
- 只是
new出来的对象 - 还没调用
start() - 和 OS 线程没有任何关系
RUNNABLE:可运行状态
调用 start() 后进入这个状态。注意,RUNNABLE 内部其实包含了两种情况:
- 就绪(Ready):在就绪队列里,等 CPU 调度
- 运行中(Running):正在 CPU 上执行
java
Thread thread = new Thread(() -> {
// 这个线程在 RUNNABLE 状态
// 可能是就绪,可能是运行中
System.out.println("执行中");
});
thread.start();
System.out.println(thread.getState()); // RUNNABLE重要:Java 无法区分一个 RUNNABLE 线程是「就绪」还是「运行中」。这是 OS 层面的概念。
BLOCKED:阻塞状态
等待获取 synchronized 锁时被阻塞:
java
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
// 持有锁 10 秒
try {
Thread.sleep(10_000);
} catch (InterruptedException e) {}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) { // 等着拿锁
System.out.println("拿到锁了");
}
});
thread1.start();
Thread.sleep(100); // 确保 thread1 先拿到锁
thread2.start();
System.out.println(thread2.getState()); // BLOCKED触发条件:等待 synchronized 锁时,自动进入 BLOCKED。
离开条件:锁被释放,自动被唤醒,重新竞争锁。
WAITING:无限期等待
主动调用让出 CPU,等待被唤醒:
java
// 三种方式进入 WAITING:
synchronized (lock) {
lock.wait(); // 方式1:wait()
}
thread.join(); // 方式2:join() 无参数
LockSupport.park(); // 方式3:park()java
public class WaitingDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // 无限期等待
System.out.println("被唤醒了");
} catch (InterruptedException e) {}
}
});
waitingThread.start();
Thread.sleep(100);
System.out.println(waitingThread.getState()); // WAITING
// 唤醒它
synchronized (lock) {
lock.notify();
}
}
}特点:
- 无限期等待,必须手动唤醒
- 不占用 CPU
- 不会自动醒来
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:parkNanos
condition.await(5, TimeUnit.SECONDS); // 方式5:Condition.await()java
public class TimedWaitingDemo {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000); // 等 3 秒
System.out.println("睡醒了");
} catch (InterruptedException e) {}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // TIMED_WAITING
thread.join();
}
}特点:
- 超时自动唤醒,不用手动通知
- 和 WAITING 的区别就是有没有超时参数
TERMINATED:终止状态
线程跑完了:
java
Thread thread = new Thread(() -> {
System.out.println("任务完成");
});
thread.start();
thread.join();
System.out.println(thread.getState()); // TERMINATED触发条件:
run()正常执行完毕run()抛出未捕获的异常
状态对比
| 状态 | 等待原因 | 唤醒方式 | CPU 占用 |
|---|---|---|---|
| NEW | - | start() | 否 |
| RUNNABLE | - | OS 调度 | 是 |
| BLOCKED | 等锁 | 锁释放 | 否 |
| WAITING | 主动等 | 手动唤醒 | 否 |
| TIMED_WAITING | 超时等 | 超时/唤醒 | 否 |
| TERMINATED | - | - | 否 |
代码演示:完整状态转换
java
public class ThreadStateDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread thread = new Thread(() -> {
System.out.println("1. 状态: " + Thread.currentThread().getState()); // RUNNABLE
// 进入 TIMED_WAITING
try {
Thread.sleep(500);
} catch (InterruptedException e) {}
// 进入 WAITING
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {}
}
System.out.println("4. 状态: " + Thread.currentThread().getState()); // RUNNABLE
});
System.out.println("0. 创建线程: " + thread.getState()); // NEW
thread.start();
Thread.sleep(50);
System.out.println("2. 启动后: " + thread.getState()); // TIMED_WAITING
Thread.sleep(600); // sleep 结束,等锁
synchronized (lock) {
System.out.println("3. 等待锁: " + thread.getState()); // WAITING
lock.notify();
}
thread.join();
System.out.println("5. 结束后: " + thread.getState()); // TERMINATED
}
}输出:
0. 创建线程: NEW
1. 状态: RUNNABLE
2. 启动后: TIMED_WAITING
3. 等待锁: WAITING
4. 状态: RUNNABLE
5. 结束后: TERMINATED常见问题
Q1:RUNNABLE 为什么不等于「正在运行」?
因为 OS 的「就绪」和「运行中」对 Java 来说是透明的。Java 只关心线程在不在 OS 的就绪队列里,在就是 RUNNABLE。
Java 视角:
RUNNABLE = 在 CPU 上运行 OR 在就绪队列等待
OS 视角:
Running = 正在 CPU 上执行
Ready = 在就绪队列,等调度Q2:BLOCKED 和 WAITING 的区别?
| BLOCKED | WAITING | |
|---|---|---|
| 触发方式 | 被动(等锁) | 主动(调用方法) |
| 唤醒方式 | 自动(锁释放) | 手动(notify) |
| 持有锁 | 是(拿到锁就进不来) | 否(wait 释放锁) |
Q3:sleep() 和 wait() 的区别?
| sleep() | wait() | |
|---|---|---|
| 所属 | Thread 静态方法 | Object 实例方法 |
| 释放锁 | 不释放 | 释放 |
| 位置要求 | 任何地方 | 必须在 synchronized 内 |
| 唤醒方式 | 超时 | notify / notifyAll |
总结
- 线程有 6 种状态,NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
- RUNNABLE 在 Java 中包含「就绪」和「运行中」两种 OS 状态
- BLOCKED 是等锁被动触发,WAITING 是主动调用方法
- TIMED_WAITING 是带超时的等待,到时间自动醒
- 理解状态是分析死锁、性能问题的基础
