一个ReentrantLock可配多个独立Condition实现精准唤醒:每个任务类型对应专属Condition(如canRunA),signal()仅唤醒对应await线程;需先await后signal,用while循环防虚假唤醒;await必须在lock.lock()后调用,finally中unlock()不可省略;signal()适用于单线程等待场景,signalAll()慎用。

靠一个 ReentrantLock 配多个 Condition,就能让不同任务的线程只响应自己该被唤醒的信号,不互相干扰。
每个任务类型配一个专属 Condition
比如 A、B、C 三个线程要严格按顺序交替执行,就为每个阶段建一个 Condition:conditionA 对应“A 执行完后唤醒 B”,conditionB 对应“B 执行完后唤醒 C”,conditionC 对应“C 执行完后唤醒 A”。它们都来自同一个 lock,但队列彼此隔离,signal() 只影响对应队列里的线程。
- 命名要明确,如 canRunA、canRunB、canRunC,避免 cond1/cond2 这类模糊名
- 不能混用:conditionA.signal() 永远唤不醒在 conditionB.await() 中等待的线程
- 一个 lock 可以调用多次 newCondition(),每次返回独立实例,互不干扰
唤醒前确保目标线程已在等待
signal() 不会排队,如果目标线程还没执行到 await() 就提前 signal,这个信号直接丢失。所以逻辑上必须先让线程进入 await 状态,再由前序任务触发 signal。
- 典型写法是用 while 循环判断状态,例如 while (state != EXPECTED) { condition.await(); }
- 避免用 if,否则可能因虚假唤醒或信号丢失导致跳过等待
- 调试时可用 Thread.getState() 查看线程是否真处于 WAITING 状态,但生产环境别依赖它
每次 await 必须在 lock.lock() 之后、unlock() 之前
Condition 不能脱离锁单独使用。await() 会自动释放当前持有的 lock,并在被唤醒后重新竞争获取它——所以后续代码仍在临界区内,无需额外加锁。
立即学习“Java免费学习笔记(深入)”;
- 漏掉 lock.lock() 就调 await()?线程会永久阻塞,不会抛 IllegalMonitorStateException
- 别在 synchronized 方法里混用 Condition,语义冲突,编译虽通过但行为不可控
- finally 块中 unlock() 是必须的,否则锁泄漏
选 signal() 还是 signalAll() 要看场景
如果同一 Condition 上只允许一个线程等待(比如轮转执行模型),用 signal() 更高效;如果多个同类线程都可能等待(比如多个消费者等数据),且状态变更对所有都有效,才考虑 signalAll()。
- signal() 只唤醒队列头部一个线程,适合“一人一岗”式协作
- signalAll() 会唤醒全部,但所有被唤醒线程都要重新抢锁、再检查条件,容易引发惊群效应
- 不要为了“保险”而滥用 signalAll(),精准唤醒的价值正在于减少无效竞争


















