reentrantlock 配合 condition 可实现线程精准唤醒,因每个 condition 维护独立等待队列,signal() 仅唤醒在其上 await() 的线程;需用 while 循环条件检查、持锁调用 signal、不同语义场景使用不同 condition。

ReentrantLock 配合 Condition 可以实现线程的精准唤醒,避免 synchronized 中 notify() 唤醒任意一个等待线程带来的不确定性。核心在于:每个 Condition 对象绑定到同一个锁上,但维护独立的等待队列,调用其 signal() 或 signalAll() 时,只唤醒在该 Condition 上 await() 的线程。
一、Condition 的基本使用逻辑
ReentrantLock 不像 synchronized 内置 monitor 那样自带 wait/notify,它把等待/唤醒机制抽离成 Condition 接口。一个 Lock 可创建多个 Condition 实例,每个代表一类等待场景:
- 调用
lock.newCondition()获取 Condition 实例 - 线程必须先获取锁,才能调用该 Condition 的
await()(会自动释放锁,并进入对应等待队列) - 其他线程在持有同一把锁的前提下,调用
signal()唤醒一个等待线程,或signalAll()唤醒全部 -
await()返回前会重新竞争锁,成功后才继续执行(需配合 while 循环做条件重检)
二、典型场景:生产者-消费者精准唤醒
比如缓冲区满时,生产者 await(full),消费者消费后 signal(full);缓冲区空时,消费者 await(empty),生产者生产后 signal(empty)。两个等待队列互不干扰:
class BoundedBuffer {
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final List<integer> buffer = new ArrayList();
private final int capacity = 10;
void put(int item) throws InterruptedException {
lock.lock();
try {
while (buffer.size() == capacity) {
notFull.await(); // 等在 notFull 队列
}
buffer.add(item);
notEmpty.signal(); // 只唤醒在 notEmpty 上 await 的消费者
} finally {
lock.unlock();
}
}
int take() throws InterruptedException {
lock.lock();
try {
while (buffer.isEmpty()) {
notEmpty.await(); // 等在 notEmpty 队列
}
int item = buffer.remove(0);
notFull.signal(); // 只唤醒在 notFull 上 await 的生产者
return item;
} finally {
lock.unlock();
}
}
}
</integer>
三、关键注意事项
精准唤醒不是自动发生的,依赖正确使用模式:
- 必须用 while 而非 if 检查条件:防止虚假唤醒或被错误 signal 后条件仍不满足
- signal() 前必须持有锁:否则抛 IllegalMonitorStateException(这是和 Object.wait/notify 的重要区别)
- 不同语义的等待应使用不同 Condition:比如“有数据可读”和“有空间可写”不能共用一个 Condition,否则 signal() 会唤醒错的线程
- await() 会释放锁,signal() 不释放锁:signal() 后当前线程继续执行,被唤醒线程需重新竞争锁
四、进阶:多路等待与选择性唤醒
一个锁可以关联多个 Condition,适用于更细粒度的协作。例如任务调度器中:
-
readyTasks:有就绪任务待执行 -
idleWorkers:有空闲工作线程可分配任务 -
shutdownSignal:收到关闭指令
当新任务到达,只 readyTasks.signal();当线程变空闲,只 idleWorkers.signal();关闭时三个都 signalAll()。各线程按需 await 对应 Condition,真正实现“谁该醒谁醒”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











