signal() 不等于唤醒符合条件的线程,它仅唤醒等待队列头部线程;真正精准依赖while循环重检条件、状态更新与signal同锁保护、各业务状态配独立condition实例。

Condition.signal 本身不判断条件是否满足,它只负责唤醒等待队列头部的一个线程;真正“精准”的关键,在于你如何组织等待逻辑、条件检查和 signal 的触发时机。
为什么 signal() 不等于“唤醒符合条件的线程”
signal() 只是把 await() 队列里排最前面的线程叫醒,它不看这个线程等的是什么、当前状态是否真适合执行。被唤醒的线程一睁眼,第一件事必须是重新获取锁,然后立刻用 while 循环再次检查业务条件——不是 if。
- 错误写法:
if (!orderPaid) { paymentConfirmed.await(); }→ 虚假唤醒后直接往下跑,逻辑错乱 - 正确写法:
while (!orderPaid) { paymentConfirmed.await(); }→ 即使被误唤醒,也会再查一遍 - signal() 前不确认目标线程已在
await()状态,信号就丢了(比如生产者先 signal,消费者还没来得及 await)
每个业务语义配一个 Condition,别混用
库存充足、订单已支付、物流已发货——这些是完全独立的业务状态,必须各自声明独立的 Condition 实例,比如:
private final Condition inventoryAvailable = lock.newCondition(); private final Condition paymentConfirmed = lock.newCondition(); private final Condition shipmentDispatched = lock.newCondition();
-
inventoryAvailable.signal()只唤醒在inventoryAvailable.await()上等库存的线程,对其他 Condition 完全无感 - 别试图用一个
generalCond.signal()同时通知“库存够了”和“钱付了”——它们根本不在同一个队列里 - 命名要直白,
cond1这种变量名在调试时会让你怀疑人生
signal() 前必须确保共享状态已更新且可见
Condition 依赖的共享变量(比如 orderPaid、stockLevel)必须被同一把 ReentrantLock 保护,或者至少是 volatile;否则 signal 发出时,被唤醒线程看到的可能是过期值。
- 更新状态和调用
signal()必须在同一个lock.lock()/unlock()块内完成 - 不要在 unlock 后才改状态再 signal —— 中间可能有其他线程插进来破坏一致性
- 示例片段:
lock.lock();
try {
orderPaid = true; // 状态更新
paymentConfirmed.signal(); // 紧接着唤醒
} finally {
lock.unlock();
}
signal() 和 signalAll() 别选错场景
单个资源就绪(比如一个新订单生成),只够一个消费者处理,就用 signal();状态变更影响所有等待者(比如系统全局暂停结束),才用 signalAll()。
-
signal()开销小,但要求你明确知道“谁该被叫醒”,适合强顺序或独占型业务 -
signalAll()会唤醒全部,所有线程抢锁、重判条件,容易引发惊群效应;别为了图省事全用它 - 需要“广播给多个 Condition”,得手动对每个调用
signalAll(),没有通用广播接口
最容易被忽略的一点:signal() 的“精准”不是靠它自己实现的,而是靠你把条件检查、状态更新、signal 触发这三步绑死在同一把锁下,并让每个业务路径守自己的 Condition 队列——漏掉任意一环,唤醒就变成碰运气。










