java-Exchanger详解

2024-01-11 01:52
文章标签 java 详解 exchanger

本文主要是介绍java-Exchanger详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.概述

java.util.concurrent.Exchanger。这在Java中作为两个线程之间交换对象的公共点。

2.Exchanger简介

Exchanger类可用于在两个类型为T的线程之间共享对象。该类仅提供了一个重载的方法exchange(T t)。

当调用exchanger时,它会等待成对的另一个线程也调用它。在这一点上,第二个线程发现第一个线程正在等待其对象。线程交换它们持有的对象并发出交换信号,然后它们可以返回。

让我们看一个例子,以理解两个线程之间使用Exchanger进行消息交换:

 @Test
public void givenThreads_whenMessageExchanged_thenCorrect() {Exchanger<String> exchanger = new Exchanger<>();Runnable taskA = () -> {try {String message = exchanger.exchange("from A");assertEquals("from B", message);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}};Runnable taskB = () -> {try {String message = exchanger.exchange("from B");assertEquals("from A", message);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}};CompletableFuture.allOf(runAsync(taskA), runAsync(taskB)).join();}

在这里,我们有两个线程使用共同的Exchanger交换彼此之间的消息。让我们看一个例子,在这个例子中,我们从主线程与一个新线程交换对象:

@Test
public void givenThread_WhenExchangedMessage_thenCorrect() throws InterruptedException, ExecutionException {Exchanger<String> exchanger = new Exchanger<>();Runnable runner = () -> {try {String message = exchanger.exchange("from runner");assertEquals("to runner", message);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}};CompletableFuture<Void> result = runAsync(runner);String msg = exchanger.exchange("to runner");assertEquals("from runner", msg);result.join();}

请注意,我们需要先启动runner线程,然后在主线程中调用exchange()

还要注意,如果第二个线程在超时时间内未达到交换点,第一个线程的调用可能会超时。第一个线程应等待多长时间可以使用重载的exchange(T t, long timeout, TimeUnit timeUnit)来控制。

3.无GC数据交换

Exchanger可以用于创建通过一个线程向另一个线程传递数据的管道类型的模式。

   private static final int BUFFER_SIZE = 100;@Testpublic void givenData_whenPassedThrough_thenCorrect() throws InterruptedException, ExecutionException {Exchanger<Queue<String>> readerExchanger = new Exchanger<>();Exchanger<Queue<String>> writerExchanger = new Exchanger<>();int counter = 0;Runnable reader = () -> {Queue<String> readerBuffer = new ConcurrentLinkedQueue<>();while (true) {readerBuffer.add(UUID.randomUUID().toString());if (readerBuffer.size() >= BUFFER_SIZE) {try {readerBuffer = readerExchanger.exchange(readerBuffer);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}}}};Runnable processor = () -> {Queue<String> processorBuffer = new ConcurrentLinkedQueue<>();Queue<String> writerBuffer = new ConcurrentLinkedQueue<>();try {processorBuffer = readerExchanger.exchange(processorBuffer);while (true) {writerBuffer.add(processorBuffer.poll());if (processorBuffer.isEmpty()) {try {processorBuffer = readerExchanger.exchange(processorBuffer);writerBuffer = writerExchanger.exchange(writerBuffer);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}}}} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}};Runnable writer = () -> {Queue<String> writerBuffer = new ConcurrentLinkedQueue<>();try {writerBuffer = writerExchanger.exchange(writerBuffer);while (true) {System.out.println(writerBuffer.poll());if (writerBuffer.isEmpty()) {writerBuffer = writerExchanger.exchange(writerBuffer);}}} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException(e);}};CompletableFuture.allOf(runAsync(reader), runAsync(processor), runAsync(writer)).get();}

在这里,我们有三个线程:readerprocessorwriter。它们共同作为一个单一的管道,在它们之间交换数据。

readerExchangerreaderprocessor线程之间共享,而writerExchangerprocessorwriter线程之间共享。

请注意,此处的示例仅用于演示。在创建无限循环时务必小心while(true)。另外,为保持代码的可读性,我们省略了一些异常处理。

通过重用缓冲区来交换数据的这种模式允许减少垃圾回收。exchange方法返回相同的队列实例,因此这些对象不会被垃圾回收。与任何阻塞队列不同,Exchanger不会创建任何用于保存和共享数据的节点或对象。

创建这样的管道类似于Disruptor模式,其中一个关键区别是,Disruptor模式支持多个生产者和消费者,而Exchanger可以在一对生产者和消费者之间使用。

4.总结

因此,Java中的Exchanger是什么,它是如何工作的,我们看到了如何使用Exchanger类。此外,我们创建了一个管道,并演示了线程之间无GC的数据交换。

这篇关于java-Exchanger详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/592771

相关文章

Java数组初始化的五种方式

《Java数组初始化的五种方式》数组是Java中最基础且常用的数据结构之一,其初始化方式多样且各具特点,本文详细讲解Java数组初始化的五种方式,分析其适用场景、优劣势对比及注意事项,帮助避免常见陷阱... 目录1. 静态初始化:简洁但固定代码示例核心特点适用场景注意事项2. 动态初始化:灵活但需手动管理代

Java使用SLF4J记录不同级别日志的示例详解

《Java使用SLF4J记录不同级别日志的示例详解》SLF4J是一个简单的日志门面,它允许在运行时选择不同的日志实现,这篇文章主要为大家详细介绍了如何使用SLF4J记录不同级别日志,感兴趣的可以了解下... 目录一、SLF4J简介二、添加依赖三、配置Logback四、记录不同级别的日志五、总结一、SLF4J

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Java 正则表达式URL 匹配与源码全解析

《Java正则表达式URL匹配与源码全解析》在Web应用开发中,我们经常需要对URL进行格式验证,今天我们结合Java的Pattern和Matcher类,深入理解正则表达式在实际应用中... 目录1.正则表达式分解:2. 添加域名匹配 (2)3. 添加路径和查询参数匹配 (3) 4. 最终优化版本5.设计思

Java使用ANTLR4对Lua脚本语法校验详解

《Java使用ANTLR4对Lua脚本语法校验详解》ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,下面就跟随小编一起看看Java如何使用ANTLR4对Lua脚本... 目录什么是ANTLR?第一个例子ANTLR4 的工作流程Lua脚本语法校验准备一个Lua Gramm

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

基于Java实现回调监听工具类

《基于Java实现回调监听工具类》这篇文章主要为大家详细介绍了如何基于Java实现一个回调监听工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录监听接口类 Listenable实际用法打印结果首先,会用到 函数式接口 Consumer, 通过这个可以解耦回调方法,下面先写一个

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析