ReentrantLock 配合多个 Condition 可实现精准分组唤醒,避免 notifyAll 的无效竞争;每个 Condition 关联独立等待队列,signal() 仅唤醒对应队列线程,如生产者-消费者模型中 notFull 和 notEmpty 分别管理两类等待者。

ReentrantLock 配合多个 Condition 可以实现**精准分组唤醒**,避免 synchronized 中 notifyAll() 唤醒所有等待线程带来的无效竞争和资源浪费。
为什么需要多个 Condition
单个锁(ReentrantLock)默认只提供一个 wait/notify 语义(通过 lock.newCondition() 创建的 Condition 实例)。但实际业务中,常存在多种等待条件(比如“队列非空”和“队列未满”),不同线程因不同原因阻塞,理应由不同信号触发唤醒。多个 Condition 就是为这类场景设计的——每个 Condition 关联独立的等待队列,signal() 只唤醒该队列中的线程。
典型用法:生产者-消费者模型中的双 Condition
以有界队列为例,需区分两类等待者:
- 生产者在队列满时 await(notFull),被 notFull.signal() 唤醒
- 消费者在队列空时 await(notEmpty),被 notEmpty.signal() 唤醒
代码结构示意:
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition(); // 消费者等待
private final Condition notFull = lock.newCondition(); // 生产者等待
private final Queue<object> queue = new ArrayDeque();
private final int capacity = 10;
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 队列满 → 等待 notFull 信号
}
queue.offer(item);
notEmpty.signal(); // 入队成功 → 唤醒一个等待消费的线程
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空 → 等待 notEmpty 信号
}
Object item = queue.poll();
notFull.signal(); // 出队成功 → 唤醒一个等待生产的线程
return item;
} finally {
lock.unlock();
}
}
</object>
关键细节与注意事项
使用多个 Condition 时,必须注意以下几点,否则容易导致死锁或漏唤醒:
- 每个 await() 必须在对应逻辑的 while 循环中调用:防止虚假唤醒(spurious wakeup)和条件变更(如多个生产者同时 await 后,仅一个被 signal,其余需重新检查)
- signal() 不保证唤醒“刚进入等待”的线程:它唤醒的是当前在该 Condition 队列头部的线程;若需唤醒全部,用 signalAll()
- Condition 必须由持有锁的线程调用:await() 会自动释放锁,并在被唤醒后重新竞争锁;signal() 也必须在加锁状态下调用
- 一个 Condition 只能绑定到一个 Lock:不能把 A 锁创建的 Condition 用于 B 锁的同步块中
扩展:三个及以上 Condition 的适用场景
当业务状态更复杂时,可进一步拆分等待维度。例如任务调度器中:
- waitingForResource:等待某类资源就绪
- waitingForTimeout:等待超时时间到达
- waitingForPriorityBoost:等待高优先级任务插入
每个 Condition 管理一类等待者,signal 调用可按事件类型精准触发,显著提升响应性和吞吐量。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











