reentrantlock 配合 condition 实现线程精准唤醒:signal() 将等待队列首节点移入 aqs 同步队列,真正唤醒需满足持有锁、成功获取锁、从 await() 返回;所有操作须在 lock 保护下进行,且 await 必须用 while 循环防虚假唤醒。

ReentrantLock 配合 Condition 使用,是 Java 中实现线程间精准唤醒(替代 Object 的 wait/notify)的核心方式。调用 condition.signal() 不会立即唤醒线程,它只是将等待队列中一个处于 WAITING 状态的线程移入 AQS 同步队列,后续由锁释放时“顺带”唤醒。真正被唤醒并继续执行,还需满足:持有锁、成功获取锁、从 await() 返回。
condition.signal() 的实际唤醒流程
Condition 是 ReentrantLock 的“等待队列控制器”,每个 Condition 对象维护一个独立的 FIFO 等待队列。signal() 的作用是:
- 从该 Condition 的等待队列中取出队首节点(即最早调用 await() 的线程)
- 将其从等待队列转移到 AQS 同步队列(即主锁的竞争队列)
- 若此时锁已释放,且该节点是同步队列头节点,则可能被 unpark 唤醒;否则需等前序节点释放锁后依次唤醒
必须和 await() 成对使用,且在 lock 保护下
Condition 的所有操作(await、signal、signalAll)都要求当前线程已持有对应 ReentrantLock。否则抛 IllegalMonitorStateException。
典型安全写法:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
lock.lock();
try {
while (!conditionMet) { // 必须用 while,防虚假唤醒
condition.await();
}
// 执行业务逻辑
} finally {
lock.unlock();
}
对应唤醒端也必须加锁:
lock.lock();
try {
conditionMet = true;
condition.signal(); // 此时才真正触发唤醒准备
} finally {
lock.unlock();
}
signal() 和 signalAll() 的关键区别
signal() 只唤醒一个等待线程(不保证是哪个,但通常是队首),适合“单生产者-单消费者”或只需唤醒一个处理者的场景;signalAll() 唤醒所有等待该 Condition 的线程,适合广播通知(如状态变更影响多个观察者)。
注意:即使只 signal() 一个,也不能假设其他线程仍等待——它们可能已被中断、超时返回、或被其他 signal 唤醒过。所以 await 外层一定要用 while 循环判断条件,而非 if。
常见误区与调试提示
- 没加 lock 就调 signal() → 抛异常,程序中断
- signal() 后没 unlock() → 唤醒的线程卡在同步队列里,无法获取锁,永远阻塞
- await() 前没检查条件 → 可能跳过唤醒,导致死等(用 while 避免)
- 多个 Condition 共用一把锁时,signal() 只影响对应 Condition 的等待队列,互不干扰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










