“wait on condition”表示线程主动挂起等待条件变量唤醒,非卡死;其thread dump特征为waiting (on object monitor)、堆栈含object.wait()/condition.await()、并明确标注等待对象地址。

“Wait on condition” 不代表线程卡死,而是它正在等待某个条件变量被唤醒——本质是 Object.wait()、Condition.await() 或 JVM 内部锁机制触发的主动挂起。
怎么从 thread dump 里认出 Wait on condition
在 jstack 或 jcmd <pid> VM.native_threads</pid> 输出中,看到类似这样的线程状态行:
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000712345678> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)注意两个关键信号:
- 线程状态是
WAITING(不是BLOCKED),且括号里明确写着(on object monitor)或(parking to wait for ...) - 堆栈顶部是
Object.wait()、Condition.await()、LockSupport.park()这类阻塞调用 - 下一行带
- waiting on—— 这个地址就是它等的那个Object或Condition实例
Wait on condition 和 BLOCKED 的根本区别
BLOCKED 是抢不到 monitor 锁(比如 synchronized 方法入口),纯竞争问题;而 Wait on condition 是线程自己调用 wait() 或 await() 主动放弃 CPU,等别人 notify() 或 signal() —— 它不争锁,只等通知。
立即学习“Java免费学习笔记(深入)”;
-
BLOCKED线程堆栈通常停在synchronized块或方法入口,没有wait/await调用 -
WAITING (on object monitor)必定有明确的等待对象(),且该对象大概率被其他线程持有并可能负责唤醒 - 如果等的是
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject,说明用的是ReentrantLock.newCondition(),不是传统 synchronized
为什么它常被误判为“死锁”?常见坑点
很多同学看到一堆线程都停在 Object.wait() 就慌了,以为系统僵住了。其实绝大多数情况是正常协作逻辑——比如生产者-消费者模型里,消费者空队列时就 wait(),等生产者 notify()。
- 真正危险的是:没人负责
notify()/signal()—— 比如逻辑漏掉唤醒、用错对象(notify()在 A 对象上调用,但线程等在 B 对象上) - 用
ReentrantLock+Condition时,必须确保signal()和await()用的是同一个Condition实例,否则完全无效 -
wait(timeout)带超时是安全兜底,但wait()无参版本一旦漏 notify,就真等 forever —— 生产环境慎用 - JVM 线程 dump 不会告诉你“谁该 notify”,只能靠你顺藤摸瓜:找到那个
对象,看哪个线程正持有它并可能执行唤醒逻辑
排查时盯住这三样东西
别光扫线程状态,重点交叉比对:
- 等待对象地址:
—— 全局搜这个地址,看有没有线程正synchronized它,或者调用它的notifyAll() - 唤醒路径是否可达:比如
notify()写在 if 分支里,但条件永远不满足,那等于没写 - 锁层级是否嵌套错乱:在 synchronized 块里调
wait()是合法的,但如果在ReentrantLock.lock()后直接调Object.wait()(而非Condition.await()),会导致无法唤醒且不报错
最麻烦的情况是多个 Condition 共享一个 lock,但 signal 顺序和 await 顺序不匹配,导致某些线程永远收不到通知——这种得结合业务逻辑手动画状态转移图才看得清。








