ReentrantLock 配合 Condition 实现等待唤醒机制,支持多等待队列、可中断与超时等待;需在加锁后调用 await()(自动释放锁并入队),状态更新后调用 signal() 唤醒,并用 while 循环防虚假唤醒。

ReentrantLock 配合 Condition 实现等待,核心是用 lock.newCondition() 创建条件对象,再通过 await() 挂起线程、signal()/signalAll() 唤醒指定等待队列中的线程。它比 synchronized + wait/notify 更灵活,支持多个独立等待队列、可中断等待、超时等待等。
创建锁和条件变量
先声明 ReentrantLock 实例,并调用其 newCondition() 方法获取 Condition 对象。一个锁可创建多个 Condition,各自维护独立的等待队列:
private final ReentrantLock lock = new ReentrantLock(); private final Condition notFull = lock.newCondition(); // 缓冲区未满 private final Condition notEmpty = lock.newCondition(); // 缓冲区非空
在临界区中正确使用 await 和 signal
必须在持有锁的前提下调用 await(),否则抛 IllegalMonitorStateException;调用后会自动释放锁,并将当前线程加入该 Condition 的等待队列;被唤醒后需重新竞争锁,成功获取后才从 await 返回:
- 写等待逻辑时,用 while 循环判断条件(不是 if),防止虚假唤醒
- 唤醒方应在修改共享状态后,调用
signal()或signalAll() - 操作完成后务必 unlock() —— 推荐用 try-finally 保证释放
典型生产者-消费者示例片段
以有界队列为例,展示如何协调生产和消费:
立即学习“Java免费学习笔记(深入)”;
public void put(E item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 等待“未满”条件
}
queue.offer(item);
notEmpty.signal(); // 通知至少一个消费者可以取了
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待“非空”条件
}
E item = queue.poll();
notFull.signal(); // 通知至少一个生产者可以放了
return item;
} finally {
lock.unlock();
}
}
注意点和常见陷阱
-
await()会释放当前锁,但signal()不释放锁,唤醒后需重新抢锁 - 不同 Condition 对应不同语义的等待队列,避免混用(如不能用 notFull.signal() 唤醒等待 notEmpty 的线程)
- 若需响应中断,用
await();若要超时控制,用awaitNanos(long)或awaitUntil(Date) - 不建议在循环体外直接调用 signal,应确保状态已更新且满足被唤醒线程的继续执行条件


















