blocked和waiting区别
线程被动进入的状态:当线程尝试获取synchronized 同步锁(对象监视器锁)失败(锁已被其他线程持有),就会进入 BLOCKED 状态,直到锁被释放后参与竞争。线程主动进入的状态:线程通过调用特定方法主动放弃执行权,进入 “无时限等待”,必须通过显式唤醒才能回到 RUNNABLE 状态,否则会永久等待。BLOCKED 是 “被动等锁”:抢 synchronized 锁失败后的状态,等锁释放就
·
前置知识:Java 线程的核心状态
先明确基础:Java 线程的状态定义在 Thread.State 枚举中,核心状态有 6 种,其中和 “等待 / 阻塞” 相关的是 3 种:
- BLOCKED:阻塞(被动等锁)
- WAITING:无限等待(主动等事件,需显式唤醒)
- TIMED_WAITING:限时等待(WAITING 的超时版本,如
sleep()、wait(long))
我们重点对比 BLOCKED 和 WAITING,先分别拆解,再总结区别。
一、BLOCKED(阻塞状态)
1. 核心定义
线程被动进入的状态:当线程尝试获取 synchronized 同步锁(对象监视器锁) 失败(锁已被其他线程持有),就会进入 BLOCKED 状态,直到锁被释放后参与竞争。
2. 唯一触发场景
只有一种情况会进入 BLOCKED 状态:
线程试图进入
synchronized修饰的方法 / 代码块,但该锁的所有权已被其他线程占用。
3. 关键特性
- 被动等待:线程不是主动 “选择” 等待,而是抢锁失败后的被动状态;
- 等待的目标:等待的是 “可用的 synchronized 锁”(锁被释放就有机会抢);
- 是否释放锁:本身就没拿到锁,不存在 “释放锁” 的说法;
- 唤醒方式:无需显式唤醒 —— 当持有锁的线程释放锁(退出同步块 / 方法),JVM 会自动唤醒所有争抢该锁的 BLOCKED 线程,让它们重新竞争锁;
- 和锁强绑定:只和 synchronized 锁的竞争相关,与
wait()无关。
4. 代码示例(BLOCKED 状态演示)
这个示例就是你之前看的 sleep() 不释放锁的场景,线程 2 抢锁失败进入 BLOCKED 状态:
public class BlockedStateDemo {
private static final Object lock = new Object();
public static void main(String[] args) {
// 线程1:获取锁后sleep(不释放锁)
new Thread(() -> {
synchronized (lock) {
System.out.println("线程1:获取锁,开始sleep 3秒(不释放锁)");
try {
Thread.sleep(3000); // sleep期间锁仍被持有
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程1:sleep结束,释放锁");
}
}, "线程1").start();
// 线程2:尝试抢锁,失败后进入BLOCKED状态
new Thread(() -> {
System.out.println("线程2:尝试获取锁...");
synchronized (lock) { // 抢锁失败,进入BLOCKED
System.out.println("线程2:成功获取锁(BLOCKED状态结束)");
}
}, "线程2").start();
}
}
状态变化:
- 线程 1:RUNNABLE → TIMED_WAITING(sleep)→ RUNNABLE → TERMINATED;
- 线程 2:RUNNABLE → BLOCKED(抢锁失败)→ 线程 1 释放锁后 → RUNNABLE(抢到锁)→ TERMINATED。
二、WAITING(无限等待状态)
1. 核心定义
线程主动进入的状态:线程通过调用特定方法主动放弃执行权,进入 “无时限等待”,必须通过显式唤醒才能回到 RUNNABLE 状态,否则会永久等待。
2. 触发场景(仅以下 3 种)
只有调用以下方法(无参版本)才会进入 WAITING 状态:
| 方法 | 场景说明 |
|---|---|
Object.wait() |
释放对象锁,等待同一对象的 notify()/notifyAll() 唤醒 |
Thread.join() |
等待目标线程完全结束(比如 t.join() 表示当前线程等 t 执行完) |
LockSupport.park() |
底层等待方法,需通过 LockSupport.unpark(线程) 显式唤醒 |
3. 关键特性
- 主动等待:线程是主动调用
wait()/join()等方法进入等待,而非被动抢锁失败; - 等待的目标:等待的是 “特定事件”(如被
notify()唤醒、目标线程结束),而非锁; - 是否释放锁:
- 调用
Object.wait():会释放持有的对象锁(这是核心!); - 调用
Thread.join():不会释放锁(join 本质是 wait (),但锁是目标线程对象); - 调用
LockSupport.park():不涉及 synchronized 锁,无需释放;
- 调用
- 唤醒方式:必须显式唤醒,否则永久等待:
wait()→ 需notify()/notifyAll();join()→ 目标线程结束自动唤醒;park()→ 需unpark(线程);
- 和事件强绑定:等待的是 “事件发生”,而非锁的释放。
4. 代码示例(WAITING 状态演示)
这个示例是你之前学的 wait() 场景,线程调用 lock.wait() 后进入 WAITING 状态:
public class WaitingStateDemo {
private static final Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread waitThread = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("等待线程:获取锁,调用wait()进入WAITING状态");
lock.wait(); // 释放锁,进入WAITING(需notify唤醒)
System.out.println("等待线程:被唤醒,退出WAITING状态");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "等待线程");
waitThread.start();
// 主线程等待1秒,确保waitThread进入WAITING状态
Thread.sleep(1000);
System.out.println("等待线程的状态:" + waitThread.getState()); // 输出 WAITING
// 唤醒线程:调用notify()唤醒等待线程
new Thread(() -> {
synchronized (lock) {
System.out.println("唤醒线程:获取锁,调用notify()");
lock.notify(); // 唤醒WAITING的线程
}
}, "唤醒线程").start();
}
}
状态变化:
- 等待线程:RUNNABLE → WAITING(调用
wait())→ 被 notify 后 → BLOCKED(抢锁)→ RUNNABLE → TERMINATED; - 注意:唤醒后不会直接回到 RUNNABLE,而是先进入 BLOCKED 抢锁,抢到后才执行!
三、补充:TIMED_WAITING(限时等待)
你可能会混淆这个状态,它是 WAITING 的 “超时版本”:
- 触发场景:调用带超时参数的方法,如
Thread.sleep(long)、Object.wait(long)、Thread.join(long)、LockSupport.parkNanos(); - 核心区别:无需显式唤醒,超时后自动回到 RUNNABLE 状态;
- 示例:
Thread.sleep(3000)会让线程进入 TIMED_WAITING 状态,3 秒后自动醒。
四、BLOCKED vs WAITING 核心对比表
| 维度 | BLOCKED(阻塞) | WAITING(无限等待) |
|---|---|---|
| 触发原因 | 抢 synchronized 锁失败(被动) | 主动调用 wait()/join()/park()(主动) |
| 等待的目标 | 等待 “synchronized 锁释放” | 等待 “特定事件”(notify、目标线程结束等) |
| 是否释放锁 | 未拿到锁,无锁可释放 | 调用 wait() 会释放锁,join()/park() 不释放 |
| 唤醒方式 | 锁释放后自动竞争(无需显式唤醒) | 必须显式唤醒(如 notify ()、unpark ()) |
| 典型触发方法 | 进入 synchronized 块 / 方法(抢锁失败) | Object.wait()(无参)、Thread.join()(无参)、LockSupport.park() |
| 状态转换 | BLOCKED → 抢锁成功 → RUNNABLE | WAITING → 被唤醒 → BLOCKED(抢锁)→ RUNNABLE |
五、高频易错点
- “wait () 后进入 BLOCKED” 是错误的:
wait()后线程进入 WAITING,唤醒后先进入 BLOCKED 抢锁,抢到才到 RUNNABLE; - BLOCKED 只和 synchronized 有关:Lock 锁(如 ReentrantLock)的等待线程不会进入 BLOCKED,而是进入 WAITING/TIMED_WAITING;
- sleep () 进入 TIMED_WAITING,不是 BLOCKED:新手常误以为 sleep 会让线程阻塞,实际是限时等待。
总结
- BLOCKED 是 “被动等锁”:抢 synchronized 锁失败后的状态,等锁释放就自动竞争,无需显式唤醒;
- WAITING 是 “主动等事件”:线程主动调用方法进入的状态,必须通过 notify ()/unpark () 等显式唤醒,否则永久等待;
- 核心记忆:BLOCKED 等 “锁”,WAITING 等 “事件”,TIMED_WAITING 是 WAITING 的超时版。
更多推荐

所有评论(0)