讲师中心 微信公众号
AI工具推荐 视频效率加速

Java中如何用Condition实现线程间通信

小婷大大_5577

小婷大大_5577

发布时间:2025-06-26 08:06:02

|

304人浏览过

|

来源于php中文网

原创

condition在java中确实可以看作是lock锁的增强版,它通过await()和signal()/signalall()提供更细粒度的线程同步机制。1. await()用于线程等待并释放锁;2. signal()唤醒一个等待线程,signalall()唤醒所有等待线程。与wait()/notify()相比,condition必须显式绑定到lock,支持多个等待队列,从而实现更灵活的控制。例如,在生产者-消费者模型中使用notfull和notempty两个condition分别管理缓冲区状态。使用condition时应在循环中检查条件以防止虚假唤醒或条件变化导致的错误。选择signal()还是signalall()取决于是否需要唤醒单个或多个线程,signalall()通常更安全。当需要更精细的线程控制或多等待队列时应使用condition,简单场景可继续使用wait()/notify()。

Java中如何用Condition实现线程间通信

Condition在Java中可以看作是Lock锁的增强版,它允许线程在满足特定条件时挂起(等待),并在其他线程改变条件后被唤醒。这提供了一种比wait()/notify()更细粒度的线程同步机制。

Java中如何用Condition实现线程间通信

解决方案

Java中如何用Condition实现线程间通信

Condition接口的核心在于await()(等待)和signal()/signalAll()(通知)。await()类似于Object.wait(),它释放锁并让线程进入等待状态,直到被signal()或signalAll()唤醒。signal()唤醒一个等待的线程,signalAll()唤醒所有等待的线程。

立即学习“Java免费学习笔记(深入)”;

Java中如何用Condition实现线程间通信

下面是一个简单的例子,展示了如何使用Condition实现生产者-消费者模型:

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer {

    private final Queue<Integer> buffer = new LinkedList<>();
    private final int maxSize = 10;
    private final Lock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (buffer.size() == maxSize) {
                notFull.await(); // 缓冲区已满,等待消费者消费
            }
            buffer.offer(value);
            System.out.println("Produced: " + value);
            notEmpty.signalAll(); // 通知消费者可以消费
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (buffer.isEmpty()) {
                notEmpty.await(); // 缓冲区为空,等待生产者生产
            }
            int value = buffer.poll();
            System.out.println("Consumed: " + value);
            notFull.signalAll(); // 通知生产者可以生产
            return value;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();

        // 生产者线程
        new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    pc.produce(i);
                    Thread.sleep((long) (Math.random() * 100)); // 模拟生产时间
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 消费者线程
        new Thread(() -> {
            try {
                for (int i = 0; i < 20; i++) {
                    pc.consume();
                    Thread.sleep((long) (Math.random() * 150)); // 模拟消费时间
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

Condition和wait()/notify()的区别是什么? 什么时候应该使用Condition?

wait()/notify()是Java内置的对象监视器机制的一部分,每个对象都关联一个监视器锁。Condition则必须与Lock一起使用,提供更灵活的线程同步控制。

  • 锁的绑定: wait()/notify()隐式地与对象的内置锁绑定,而Condition需要显式地与Lock关联。
  • 多个等待队列: 一个对象只有一个等待队列用于wait(),而一个Lock可以创建多个Condition对象,每个Condition对象都有自己的等待队列。这允许更细粒度的线程调度和唤醒。比如,在上面的生产者-消费者模型中,我们使用notFull和notEmpty两个Condition,分别对应缓冲区满和缓冲区空两种等待状态。
  • 灵活性: Condition提供了更丰富的API,例如awaitUninterruptibly()(不可中断的等待)、awaitNanos()(带超时时间的等待)等。

当需要更精细的线程控制,或者需要多个等待队列时,应该使用Condition。如果简单的同步需求,wait()/notify()可能就足够了。选择哪个取决于具体场景的复杂程度。

使用Condition时,为什么需要在循环中检查条件?

即使线程被signal()或signalAll()唤醒,也可能出现“虚假唤醒”(spurious wakeup),即线程在没有被通知的情况下被唤醒。此外,即使线程确实是被通知唤醒的,在它重新获得锁并继续执行之前,其他线程可能已经改变了条件。

因此,必须在循环中检查条件,确保线程在满足条件时才继续执行。在上面的例子中,while (buffer.size() == maxSize)和while (buffer.isEmpty())就是这样的循环。如果没有这些循环,可能会出现缓冲区溢出或从空缓冲区消费的情况。

Condition的signal()和signalAll()有什么区别? 应该选择哪个?

signal()唤醒一个等待在Condition上的线程,具体唤醒哪个线程取决于JVM的实现。signalAll()唤醒所有等待在Condition上的线程。

选择哪个取决于具体的需求。如果只需要唤醒一个线程来处理任务,并且知道只有一个线程可以处理,那么signal()更高效。如果多个线程都可以处理任务,或者不确定哪个线程最适合处理,那么signalAll()更安全。

在生产者-消费者模型中,使用signalAll()更安全,因为生产者可能需要唤醒多个消费者(如果缓冲区中有多个元素),而消费者可能需要唤醒多个生产者(如果缓冲区未满)。即使某个线程被唤醒后发现条件不满足,它也可以重新进入等待状态。

热门AI工具

更多
讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

334

2023.09.25

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3088

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

4549

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3729

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

406

2026.01.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

3818

2023.08.10

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

80

2026.09.30

LLVM RISC-V参数配置教程
LLVM RISC-V参数配置教程

本专题介绍LLVM对RISC-V基础ISA和扩展的支持方式,涵盖RV32、RV64、标准扩展、实验性扩展、厂商扩展、-menable-experimental-extensions和版本差异。

80

2026.09.30

LLVM IR中间表示入门指南
LLVM IR中间表示入门指南

本专题整理LLVM IR的核心概念,包括中间表示作用、模块结构、函数、基本块、SSA形式、类型系统和常见语法,帮助新手理解LLVM编译流程中的关键层。

80

2026.09.30

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn