使用Condition#await(long, TimeUnit)如何处理虚假唤醒并设置超时?
使用Condition#await(long, TimeUnit)处理虚假唤醒的惯用方式
处理带超时的Condition等待时,应对虚假唤醒的核心思路和无参版本一致:用循环持续检查目标条件,同时结合await(long, TimeUnit)的返回值判断最终退出原因。
核心逻辑说明
Condition.await(long, TimeUnit)会在三种场景下返回:
- 其他线程调用signal/signalAll,当前线程被唤醒
- 等待时间到达设定的超时阈值
- 当前线程被中断
虚假唤醒属于第一种场景,但此时目标条件(比如示例中的flag)并未满足,因此必须在循环中重新检查条件——只要条件不满足,就继续调用await(剩余超时时间会自动计算),直到条件满足或者超时。
修改后的代码实现
将原有的waitForever方法修改为带5秒超时的版本,同时保留应对虚假唤醒的循环逻辑:
import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Scratch { private final Lock lock = new ReentrantLock(); private final Condition flagSet = lock.newCondition(); private boolean flag; boolean waitWithTimeout() throws InterruptedException { lock.lock(); try { // 循环检查条件,未满足则继续等待(最多累计5秒) while (!flag) { // await返回false代表超时,直接退出循环 if (!flagSet.await(5, TimeUnit.SECONDS)) { break; } } // 返回最终条件是否满足 return flag; } finally { lock.unlock(); } } void setFlag() { lock.lock(); try { flag = true; System.out.println("Flag set to true"); flagSet.signalAll(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { var scratch = new Scratch(); var executor = Executors.newSingleThreadScheduledExecutor(); // 这里把延迟改成3秒,测试条件满足的情况;改成6秒则测试超时情况 executor.schedule(scratch::setFlag, 3, TimeUnit.SECONDS); System.out.println("Waiting for flag (max 5 seconds)..."); boolean flagMet = scratch.waitWithTimeout(); if (flagMet) { System.out.println("Finished waiting: flag was set"); } else { System.out.println("Finished waiting: timeout reached"); } executor.shutdownNow(); } }
关键修改点
- 将
flagSet.await()替换为flagSet.await(5, TimeUnit.SECONDS),并接收返回值:- 返回
true:表示在超时前被唤醒,需要重新检查条件 - 返回
false:表示已超时,直接退出循环
- 返回
- 保留
while (!flag)的循环逻辑:无论正常唤醒还是虚假唤醒,只要条件不满足,就继续等待(剩余超时时间会由Condition内部自动维护) - 方法返回
boolean值,方便调用者判断是条件满足还是超时退出
内容的提问来源于stack exchange,提问作者David O'Loughlin
相关产品推荐
相关产品推荐

