必须用while而非if包裹Condition.await(),因await()可能虚假唤醒或被意外signal(),需循环重检条件;标准守卫模式为:lock.lock()→try{while(!cond) await()}→操作→unlock()。

不能直接用 while 循环配合 Condition.await() 实现“精确顺序执行控制”,因为 await() 本身会释放锁并挂起当前线程,它不提供轮询或主动等待的语义——这是常见误解。真正可靠的方式是:用 while 循环**包裹条件检查 + await()**,即经典的「守卫条件(guarded block)」模式,确保线程只在满足业务条件时才继续,且能安全响应虚假唤醒。
为什么必须用 while 而不是 if?
Condition.await() 可能因虚假唤醒(spurious wakeup)或被意外 signal() 提前返回,此时线程持有的条件未必成立。若仅用 if 判断一次,就会跳过条件校验,导致逻辑错误。
- 虚假唤醒是 JVM 规范允许的行为,无需原因就可能唤醒线程
-
signalAll()会唤醒所有等待者,但往往只有部分满足条件 - 业务状态可能在 await 返回后、临界区执行前被其他线程修改
标准写法:while + await 的守卫结构
核心模板如下(以两个线程交替打印为例):
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
lock.lock();
try {
while (!shouldProceed()) { // ✅ 必须是 while
condition.await();
}
// 此处才执行实际操作(如打印、处理数据)
} finally {
lock.unlock();
}其中 shouldProceed() 是具体业务判断逻辑,比如:
- 线程 A 等待“轮到自己”(如 turn == 0)
- 线程 B 等待“轮到自己”(如 turn == 1)
每次操作完成后更新共享状态(如 turn = 1 - turn),并调用 condition.signal() 唤醒下一个等待者。
完整示例:两线程按序打印 0 和 1
目标:线程 A 打印 0,线程 B 打印 1,严格交替(0,1,0,1,…)
class OrderedPrinter {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private int turn = 0; // 0 表示 A 可执行,1 表示 B 可执行
<pre class="brush:php;toolbar:false;">public void printA() throws InterruptedException {
lock.lock();
try {
while (turn != 0) {
condition.await();
}
System.out.print("0");
turn = 1;
condition.signal(); // 唤醒 B
} finally {
lock.unlock();
}
}
public void printB() throws InterruptedException {
lock.lock();
try {
while (turn != 1) {
condition.await();
}
System.out.print("1");
turn = 0;
condition.signal(); // 唤醒 A
} finally {
lock.unlock();
}
}}
注意:
- 每次 await() 前都重新检查 turn,避免误执行
- signal() 放在临界区内,确保状态更新与唤醒原子关联
- 不要用 signalAll()(除非需唤醒多个符合条件者),避免无谓竞争
关键注意事项
要让这种控制真正“精确”,还需注意:
- 所有对共享状态(如
turn)的读写,必须在同一个Lock保护下进行 -
await()必须在循环内调用,且循环条件必须基于可变的共享状态 - 每次状态变更后,必须显式调用
signal()或signalAll(),否则等待线程永远阻塞 - 避免在
await()前加额外 sleep 或 busy-wait,这破坏了 Condition 的设计本意,也浪费 CPU

















