Condition对象配合ReentrantLock实现精确唤醒:每个Condition绑定一个锁,await()释放锁并入等待队列,signal()/signalAll()按需唤醒指定队列线程;一个锁可创建多个独立Condition实例,支持分类等待与唤醒;await()须在lock()后调用且自动释放锁,唤醒后需重新竞争锁;必须用while循环检查条件以防虚假唤醒。

Condition 对象配合 ReentrantLock 实现精确唤醒,核心在于:每个 Condition 绑定到一个锁上,通过 await() 主动释放锁并进入等待队列,再用 signal() 或 signalAll() 唤醒指定等待队列中的线程,从而避免 synchronized 中 notify() 的“随机唤醒”问题。
一个锁可创建多个独立 Condition
ReentrantLock 支持调用多次 newCondition() 创建多个 Condition 实例,每个 Condition 拥有自己独立的等待队列。这是实现“按条件分类等待、按需唤醒”的基础。
- 比如生产者-消费者场景中,可分别定义 notFull 和 notEmpty 两个 Condition
- 当缓冲区满时,生产者 await(notFull);当空时,消费者 await(notEmpty)
- 消费者消费后 signal(notFull),只唤醒等待“不满”的生产者,不会误唤醒其他消费者
await() 必须在 lock() 后调用,且会自动释放锁
调用 await() 前必须已持有锁(否则抛 IllegalMonitorStateException),而一旦进入等待,当前线程会原子性地释放锁,并被加入该 Condition 的等待队列。
- 释放锁是自动完成的,无需手动 unlock()
- 被 signal 唤醒后,线程会重新竞争锁;只有成功获取锁后,await() 才返回
- 因此 await() 返回时,一定已重新持有锁,可安全操作共享状态
signal() 唤醒单个等待线程,signalAll() 唤醒全部
signal() 从当前 Condition 的等待队列中选一个线程唤醒(FIFO 顺序),signalAll() 则唤醒全部。选择哪个取决于业务语义是否允许多个线程同时满足条件。
- 若只有一个线程能真正推进状态(如缓冲区仅腾出一个空位),用 signal() 更高效
- 若状态变化可能让多个等待者都就绪(如批量通知),用 signalAll()
- 注意:signal() 不立即移交锁,被唤醒线程仍需竞争锁,不会导致“虚假唤醒”
务必在循环中检查条件,防止虚假唤醒或条件失效
即使被 signal 唤醒,也不能假设条件一定成立——可能被中断、超时、或其它线程抢先修改了状态。所以必须用 while 循环重检条件。
- 错误写法:
if (!canProceed) condition.await(); - 正确写法:
while (!canProceed) condition.await(); - 典型模式:加锁 → 检查条件 → 不满足则 await() → 被唤醒后继续循环检查 → 满足则执行业务逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











