当前位置: 首页 > article >正文

Java 入门指南:Java 并发编程 —— 同步工具类 Exchanger(交换器)

文章目录

    • 同步工具类
    • Exchanger
      • 工作机制
      • 特点
      • 使用步骤
      • 适用场景
      • 性能与优化
      • 使用示例

同步工具类

JUC(Java.util.concurrent)是 Java 提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别是在多线程和网络通信方面。这些工具类提供了丰富的功能,帮助开发者高效地实现复杂的并发控制和网络通信需求。

![[JUC Communication Utilities.png]]

Exchanger

Exchanger(交换器)是Java中的一种同步辅助类,用于两个线程之间进行数据交换。它提供了一个同步点,当两个线程都到达该点时,它们可以交换数据并继续执行。

Exchanger 的使用场景通常是需要两个线程之间进行数据交换,其中一个线程作为生产者,另一个线程作为消费者。两个线程在交换点处进行阻塞等待,当两个线程都到达交换点时,它们可以交换自己的数据。

工作机制

  • 同步点等待:当一个线程调用 Exchanger 的 exchange() 方法时,它会在这个同步点上等待,直到另一个线程也调用 exchange() 方法。

  • 数据交换:当两个线程都到达同步点时,它们会交换各自传递给 exchange() 方法的数据。

  • 继续执行:数据交换完成后,两个线程会各自继续执行后续的任务。

特点

  • 线程间数据交换Exchanger 专门用于两个线程之间的数据交换,提供了一种简单而高效的线程间协作方式。

  • 阻塞特性Exchanger 的交换操作是阻塞的,这保证了数据交换的原子性和线程间的同步。

  • 泛型支持Exchanger 是一个泛型类,可以交换任何类型的对象,提供了极大的灵活性。

使用步骤

使用 Exchanger 的基本步骤如下:

  1. 创建一个 Exchanger 对象。

  2. 在生产者线程中,调用 exchange(),将要交换的数据传递给消费者线程,并等待对方的数据。

  3. 在消费者线程中,同样调用 exchange(),将要交换的数据传递给生产者线程,并等待对方的数据。

  4. 当两个线程都调用了 exchange() 方法后,它们会交换数据并继续执行后续操作。

需要注意的是,Exchanger 只能用于两个线程之间的数据交换,无法支持多个线程之间的交换。另外,Exchanger 只支持点对点的数据交换,每次交换只能有一个生产者和一个消费者

适用场景

Exchanger 在一些特定的场景下非常有用,例如:

  • 数据传输:当一个线程需要将数据传输给另一个线程时,可以使用 Exchanger 进行数据交换。例如,在文件传输、网络通信等场景中,可以使用 Exchanger 实现数据的双向传输。

  • 数据同步:当两个线程需要同步它们的数据时,可以使用 Exchanger 进行数据交换。这有助于确保数据的一致性和正确性。

  • 生产者-消费者问题Exchanger 也可以用于解决生产者-消费者问题。生产者线程可以将生产的数据交换给消费者线程,消费者线程处理后再将结果交换回生产者线程,从而实现生产者和消费者之间的紧密协作。

Exchanger 提供了灵活的线程间通信和数据交换机制,可以简化并发编程的复杂度,提高代码的可读性和可维护性。

性能与优化

  • 线程数控制Exchanger 的性能受限于线程数。过多的线程会导致频繁的线程切换和锁竞争,从而降低性能。因此,在使用Exchanger时,应合理控制线程数。

  • 数据大小控制:交换的数据大小也会影响性能。大数据量的交换会增加数据传输时间,降低性能。因此,在可能的情况下,应尽量减少交换的数据量。

  • 并发度控制:多个线程同时竞争 Exchanger 时,会增加锁的竞争,导致性能下降。因此,应合理控制并发度,避免过多的线程同时竞争 Exchanger

使用示例

下面是一个完整的示例,展示了如何使用 Exchanger 来交换数据:

import java.util.concurrent.Exchanger;
import java.util.concurrent.TimeUnit;

public class ExchangerExample {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();

        // 创建并启动生产者线程
        Thread producerThread = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is producing...");
                TimeUnit.SECONDS.sleep(2);
                String producerData = "Data from Producer";
                System.out.println(Thread.currentThread().getName() + " produced: " + producerData);
                String consumerData = exchanger.exchange(producerData);
                System.out.println(Thread.currentThread().getName() + " received: " + consumerData);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Producer");

        // 创建并启动消费者线程
        Thread consumerThread = new Thread(() -> {
            try {
                System.out.println(Thread.currentThread().getName() + " is consuming...");
                TimeUnit.SECONDS.sleep(1);
                String consumerData = "Data from Consumer";
                System.out.println(Thread.currentThread().getName() + " consumed: " + consumerData);
                String producerData = exchanger.exchange(consumerData);
                System.out.println(Thread.currentThread().getName() + " received: " + producerData);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Consumer");

        producerThread.start();
        consumerThread.start();
    }
}

当运行上述代码时,输出应该类似于:

Producer is producing...
Consumer is consuming...
Producer produced: Data from Producer
Consumer consumed: Data from Consumer
Consumer received: Data from Producer
Producer received: Data from Consumer

在这个例子中,生产者线程和消费者线程分别生成了一些数据,并通过 Exchanger 进行了交换。Exchanger 确保了两个线程在交换数据之前都处于等待状态,直到另一个线程准备好进行交换。


http://www.kler.cn/news/302950.html

相关文章:

  • 学生请假管理系统
  • pytest钩子函数
  • Ubuntu22.04安装nginx
  • SpringBoot项目用Aspose-Words将Word转换为PDF文件正常显示中文的正确姿势
  • RP2040 C SDK clocks时钟源配置使用
  • 【Kubernetes】K8s 的鉴权管理(二):基于属性 / 节点 / Webhook 的访问控制
  • 《PhysDiff: Physics-Guided Human Motion Diffusion Model》ICCV2023
  • Rust使用Actix-web和SeaORM库开发WebAPI通过Swagger UI查看接口文档
  • 若依框架使用MyBatis-Plus中的baseMapper的方法报错Invalid bound statement (not found):
  • 中电金信:金融级数字底座“源启”:打造新型数字基础设施 筑牢千行百业数字化转型发展基石
  • sponge创建的服务与dtm连接使用etcd、consul、nacos进行服务注册与发现
  • GPT-4与ChatGPT:人工智能对话的新时代【含国内可用gpt】
  • 红帽7—tomcat的部署方法
  • Unity3D Android多渠道极速打包方案详解
  • [000-01-008].第05节:OpenFeign高级特性-请求/响应压缩
  • 【油猴脚本】00003案例 Tampermonkey油猴脚本引入css 库,油猴脚本css库的使用
  • web基础之RCE
  • Ansible简单部署与使用
  • Debian项目实战——环境搭建篇
  • ctfshow-web入门-sql注入(web244-web247)error 报错注入
  • java项目之基于Spring Boot智能无人仓库管理源码(springboot+vue)
  • 《JavaEE进阶》----14.<SpringMVC配置文件实践之【验证码项目】>
  • 【聊聊AI编程必不可少的NLTK及其punkt、punkt_tab安装】
  • Python | Leetcode Python题解之第395题至少有K个重复字符的最长子串
  • WRF-LES与PALM微尺度气象大涡模拟、PALM静态数据预备、PALM驱动数据预报、PALM模拟
  • 软件交付文档
  • NAND NOR FLASH闪存产品的学习记录
  • 充电桩平台的优惠券功能如何设计
  • 【编程底层原理】Tomcat为何要打破双亲委派模式
  • 布局管理, 分割窗口, 停靠窗口, 堆栈窗口, 综合应用