Blocked 是因争锁失败被动等待锁释放,Waiting 是持锁后主动调用 wait()/join()/park() 等待通知;前者等“锁空出”,后者等“别人唤醒”。

区分 Blocked 和 Waiting 状态,关键看线程“为什么停”和“等什么”——Blocked 是抢不到锁被迫卡住,Waiting 是拿到锁后主动让出并等待通知。
触发原因完全不同
BLOCKED 只有一种成因:线程试图进入 synchronized 修饰的方法或代码块,但目标对象的监视器锁(monitor)正被其他线程持有。
- 没执行到同步块内部,连锁都没摸到
- 不涉及任何 wait()/notify()、join() 或 park()
- 哪怕只有一行
synchronized(obj) { ... },且 obj 正被占用,就进 BLOCKED
WAITING 是线程已成功获取锁,并在同步块内主动调用等待方法所致:
-
obj.wait():必须在 synchronized 块中调用,调用后立刻释放该锁并进入 WAITING -
thread.join():当前线程等待目标线程执行结束 -
LockSupport.park():底层阻塞原语,不依赖 synchronized,但需配对 unpark()
等待目标与唤醒机制差异明显
BLOCKED 线程等的是“锁空出来”:
- 等待对象:某个具体对象的 monitor 锁
- 唤醒方式:自动 —— 锁持有者退出同步块(执行完或抛异常),JVM 自动从 BLOCKED 队列选一个线程参与锁竞争
- 无需其他线程干预,也不需要 notify/park 相关操作
WAITING 线程等的是“别人来喊一声”:
- 等待对象:另一个线程执行
notify()/notifyAll()、unpark()或目标线程终止 - 唤醒方式:必须由其他线程显式触发,否则永远不醒
- 若在 synchronized 中 wait(),被 notify 后还需重新争抢同一把锁,可能再次进入 BLOCKED
状态转换路径可辅助判断
观察线程堆栈或 jstack 输出时,注意上下文线索:
- 看到
java.lang.Thread.State: BLOCKED (on object monitor)+ “waiting to lock” 字样 → 典型 BLOCKED - 看到
java.lang.Thread.State: WAITING (on object monitor)+ “waiting on” 或 “parking to wait for” → 典型 WAITING - 如果堆栈显示在
synchronized内部调用了wait(),那之前一定是 RUNNABLE,之后是 WAITING;而刚进入 synchronized 就卡住,则是 BLOCKED
一个典型对比场景
假设有两个线程 t1、t2 和共享对象 lock:
- t1 执行
synchronized(lock) { /* long work */ }→ 持有锁 - t2 执行
synchronized(lock) { ... }→ 进入 BLOCKED,等 t1 释放锁 - t1 在同步块内执行
lock.wait()→ t1 释放 lock 并进入 WAITING - t2 此时可能抢到锁,执行
lock.notify()→ t1 被唤醒,尝试重新获取 lock,若失败则先进 BLOCKED,成功才回 RUNNABLE

















