怎么利用 while 循环配合 Condition.await() 实现线程间的精确顺序执行控制

星萱同学_2624

星萱同学_2624

2026-05-09

587人浏览

原创

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

怎么利用 while 循环配合 condition.await() 实现线程间的精确顺序执行控制

不能直接用 while 循环配合 Condition.await() 实现“精确顺序执行控制”,因为 await() 本身会释放锁并挂起当前线程,它不提供轮询或主动等待的语义——这是常见误解。真正可靠的方式是:用 while 循环**包裹条件检查 + await()**,即经典的「守卫条件(guarded block)」模式,确保线程只在满足业务条件时才继续,且能安全响应虚假唤醒。

为什么必须用 while 而不是 if?

Condition.await() 可能因虚假唤醒(spurious wakeup)或被意外 signal() 提前返回,此时线程持有的条件未必成立。若仅用 if 判断一次,就会跳过条件校验,导致逻辑错误。

  • 虚假唤醒是 JVM 规范允许的行为,无需原因就可能唤醒线程
  • signalAll() 会唤醒所有等待者,但往往只有部分满足条件
  • 业务状态可能在 await 返回后、临界区执行前被其他线程修改

标准写法:while + await 的守卫结构

核心模板如下(以两个线程交替打印为例):

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,…)

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载
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

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

2023.06.15

11737

6

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

2023.07.05

8842

9

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

2023.07.31

7592

8

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.01

1244

3

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.08.02

1008

3

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

1496

5

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.02

3069

5

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

2023.08.03

20331

3

配置java环境变量
配置java环境变量

配置Java环境变量是为了让操作系统能够识别和使用Java的相关命令和功能。本专题为大家提供配置java环境变量相关文章,帮助大家解决问题。

2023.08.03

1355

8

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程