Condition.await不能替代synchronized+wait,因它仅兼容ReentrantLock而非synchronized锁,否则抛IllegalMonitorStateException;须用lock.lock()后调await(),且需while循环校验条件以防虚假唤醒。

Condition.await 为什么不能直接替代 synchronized + wait
因为 Condition.await 必须和 ReentrantLock 配合使用,它不识别 synchronized 块的锁对象。如果你在 synchronized 方法里调用 condition.await(),会抛出 IllegalMonitorStateException —— 这是最常踩的坑。
正确做法是:用 ReentrantLock 显式加锁,并通过它的 newCondition() 获取对应 Condition 实例。
-
lock.lock()后才能调用condition.await() -
await()会自动释放当前锁,并挂起线程 - 被
signal()唤醒后,线程需重新竞争锁,获取锁成功才继续执行
如何安全地等待“库存大于 0”这类业务条件
不能裸调 await(),必须包裹在 while 循环里检查条件,否则可能因虚假唤醒或条件已变而跳过校验。
典型写法是:while (!businessCondition) { condition.await(); }
比如库存扣减场景:
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
lock.lock();
try {
while (inventory <= 0) {
inventoryEmpty.await(); // 等待有库存
}
inventory--;
} finally {
lock.unlock();
}
- 用
while而非if,防止虚假唤醒后直接执行后续逻辑 - 条件判断(
inventory )和 <code>await()必须在同一个锁保护下 - 修改共享状态(如
inventory--)也必须在锁内完成,避免竞态
signal() 和 signalAll() 选哪个?业务上怎么定
如果只有一个线程在等某个条件(比如唯一消费者等库存),用 signal() 更高效;但多数业务场景中,多个线程可能同时等待同一条件(例如多个下单线程等库存释放),此时必须用 signalAll(),否则容易漏唤醒。
常见误判是认为“我只唤醒一个就够了”,但无法保证被唤醒的那个线程所需条件仍成立 —— 它醒来后仍要重新检查 while 条件,所以多唤醒几个并无实质危害,反而更健壮。
-
signal():适合严格一对一、状态变更后仅一个线程能推进的场景(如生产者-消费者模型中单个空槽位) -
signalAll():推荐作为默认选择,尤其涉及共享资源池、批量操作、不确定等待者数量时 - 唤醒操作本身必须在锁内进行,否则可能错过唤醒时机
await(long, TimeUnit) 超时后要不要重试业务逻辑
超时返回 false 并不意味着条件一定不成立,只是“等了这么久还没等到信号”。此时是否重试,取决于业务语义:
- 下单限流场景:超时可直接返回“库存获取失败”,不用重试
- 后台任务协调:超时后可能需要重新检查状态,再决定是继续 await 还是放弃
- 注意
awaitNanos返回的是剩余纳秒数,负值表示已超时;await(long, TimeUnit)返回boolean,false表示超时
超时后若要重试,仍需回到 while 循环开头再次判断条件,不能跳过校验直接操作。
真正难的不是调用 await,而是厘清谁负责改变条件、谁负责通知、以及通知之后是否真能满足等待者的预期——这些都在锁和条件变量的边界之外,却决定了整个机制是否可靠。

















