Java 锁机制中怎么利用 Condition 实现比传统 wait 更精准的条件唤醒

冷漠man

冷漠man

2026-08-04

832人浏览

原创

condition 是 reentrantlock 的配套工具,支持多等待队列和精准唤醒;一个锁可创建多个 condition 实例,各管理独立 fifo 队列,需配合 lock/unlock 使用 while 循环判断条件,优先用 signal() 实现高效线程调度。

java 锁机制中怎么利用 condition 实现比传统 wait 更精准的条件唤醒

Condition 是 ReentrantLock 的配套工具,它让线程能按具体业务条件等待和唤醒,避免了 synchronized + wait/notify 的“全唤醒”问题,从而实现比传统 wait 更精准的唤醒控制。

Condition 的核心优势:绑定多个等待队列

synchronized 只有一个内置锁对象,所有 wait() 都共用同一个等待队列,notify() 只能随机唤醒或 notifyAll() 全部唤醒。而一个 ReentrantLock 可以创建多个 Condition 实例,每个 Condition 管理独立的等待队列。比如生产者-消费者场景中,可以分别定义 notFull 和 notEmpty 两个 Condition,让满时只唤醒消费者、空时只唤醒生产者。

  • 每个 Condition 对应一个 FIFO 等待队列,互不干扰
  • await() 使当前线程进入该 Condition 的队列并释放锁;signal() 唤醒该队列头部的一个线程
  • 不会误唤醒其他业务逻辑相关的线程(如库存充足时不该唤醒补货线程)

正确使用 Condition 的三步关键操作

Condition 不是独立存在,必须与 ReentrantLock 配合,并严格遵循“加锁 → 判断条件 → await/signal → 解锁”流程。漏掉任何一环都可能引发死锁或虚假唤醒。

Java JDK 25
Java JDK 25

Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。

下载
  • 必须在 lock() 之后、条件判断之前获取 Condition 实例(不能提前缓存,否则可能关联错误锁)
  • 条件判断要用 while 而非 if:防止虚假唤醒或被 signal 后条件仍不满足,需重新检查
  • await() 前已持有锁,调用后自动释放;被 signal 唤醒后会重新竞争锁,成功获取后才从 await() 返回

典型场景示例:双条件队列的阻塞队列

下面是一个简化版的有界队列,用两个 Condition 分别管理“非满”和“非空”状态:

private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull  = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
private final List<integer> queue = new ArrayList();

public void put(int item) throws InterruptedException {
    lock.lock();
    try {
        while (queue.size() == capacity) {
            notFull.await(); // 等待“非满”条件
        }
        queue.add(item);
        notEmpty.signal(); // 有新元素,通知消费者
    } finally {
        lock.unlock();
    }
}

public int take() throws InterruptedException {
    lock.lock();
    try {
        while (queue.isEmpty()) {
            notEmpty.await(); // 等待“非空”条件
        }
        int item = queue.remove(0);
        notFull.signal(); // 拿走一个,通知生产者可继续放
        return item;
    } finally {
        lock.unlock();
    }
}</integer>

这里 notFull.signal() 和 notEmpty.signal() 各自只唤醒对应等待队列中的线程,完全规避了 notifyAll() 带来的无效调度和竞争开销。

注意 signal() 与 signalAll() 的选择

signal() 唤醒单个等待线程,适合大多数精确匹配场景;signalAll() 唤醒全部,适用于条件变更影响所有等待者(如关闭标志置位)。但要注意:即使 signal() 唤醒一个线程,它也需要重新获取锁并再次检查条件——所以无需担心“唤醒错人”,只要条件判断逻辑正确,就能自然过滤掉不满足的线程。

  • 优先用 signal(),减少上下文切换和锁竞争
  • 避免在未持有锁时调用 signal(),会抛 IllegalMonitorStateException
  • signal() 不保证唤醒顺序,但 Condition 内部队列是 FIFO,实际行为可预期

Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南

相关专题

更多
java
java

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

2023.06.15

3864

6

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

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

2023.07.05

2839

9

java自学难吗
java自学难吗

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

2023.07.31

2876

8

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

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

2023.08.01

637

3

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

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

2023.08.02

602

3

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

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

2023.08.02

726

5

java有什么用
java有什么用

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

2023.08.02

1317

5

java在线网站
java在线网站

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

2023.08.03

18845

3

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

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

2023.08.03

647

8

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
SQL优化与排查(MySQL版)
SQL优化与排查(MySQL版)

共26课时 | 2.9万人学习

Java JDBC数据库连接官方教程
Java JDBC数据库连接官方教程

共0课时 | 0人学习