Condition需配合ReentrantLock使用,通过多个条件队列实现精准线程等待与唤醒;必须在持锁下调用await/signal,且用while循环判断条件,避免虚假唤醒。

Java 中用 Condition 实现精确的线程等待与唤醒,核心在于配合 ReentrantLock 使用多个条件队列,避免 wait/notify 的“盲目唤醒”问题。它让不同业务逻辑的线程可以等待各自关心的条件,并被针对性唤醒,提升并发控制的粒度和可靠性。
Condition 必须和 ReentrantLock 配合使用
Condition 不是独立存在的,它由 Lock 实例创建(如 lock.newCondition())。不能像 synchronized + wait/notify 那样直接在任意对象上调用。一个 Lock 可以创建多个 Condition 实例,每个代表一类等待条件。
- 必须在持有锁的前提下调用
await()、signal()或signalAll() - 调用
await()会自动释放当前锁,并将线程挂起进入该Condition的等待队列 - 被
signal()唤醒后,线程需重新竞争锁,获取锁成功后才从await()返回
用不同 Condition 区分等待场景
比如生产者-消费者模型中,可为“缓冲区非空”和“缓冲区非满”分别定义 notEmpty 和 notFull 两个 Condition,避免消费者被“生产完成”信号误唤醒(而此时缓冲区仍为空)。
- 消费者只在
notEmpty.await()上等待,只响应notEmpty.signal() - 生产者只在
notFull.await()上等待,只响应notFull.signal() - 这样唤醒操作精准对应业务语义,不会出现虚假唤醒或无效唤醒
标准使用模板:加锁 → 判断条件 → 等待/执行 → 解锁
务必使用 while 循环检查条件,防止虚假唤醒(spurious wakeup)或条件变更:
立即学习“Java免费学习笔记(深入)”;
lock.lock();
try {
while (!conditionMet()) { // 不能用 if!
condition.await();
}
// 执行满足条件后的操作
} finally {
lock.unlock();
}
- 唤醒后必须重新检查条件是否真正满足,因为可能被意外唤醒,或其它线程已修改状态
-
signal()唤醒单个等待线程,signalAll()唤醒所有,按需选择 - 唤醒操作本身不要求持有锁,但通常放在临界区内,确保状态更新与唤醒原子关联
注意中断与超时支持
Condition 提供了更灵活的等待控制能力:
-
awaitNanos(long nanosTimeout):带纳秒级超时,返回剩余时间,可用于实现有界等待 -
awaitUntil(Date deadline):等待到指定时间点 -
awaitUninterruptibly():忽略中断,持续等待(慎用) - 普通
await()可响应中断,抛出InterruptedException,需正确处理
不复杂但容易忽略:Condition 的价值不在“能等”,而在“等得准、唤得对”。合理划分条件语义、严格遵循加锁-循环判断-等待模式,才能真正发挥其精确协作的优势。


















