Java多线程之同步工具类Exchanger
作者:冬日毛毛雨 发布时间:2022-07-05 03:50:54
目录
1 Exchanger 介绍
2 Exchanger 实例
exchange等待超时
3 实现原理
1 Exchanger 介绍
前面分别介绍了CyclicBarrier、CountDownLatch、Semaphore,现在介绍并发工具类中的最后一个Exchange
。Exchanger
是一个用于线程间协作的工具类,Exchanger
用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange
方法交换数据,如果第一个线程先执行exchange
方法,它会一直等待第二个线程也执行exchange
方法,当两个线程都到达同步点时,这两个线程就可以交换数据。
A synchronization point at which threads can pair and swap elements within pairs. Each thread presents some object on entry to the exchange method, matches with a partner thread, and receives its partner's object on return. An Exchanger may be viewed as a bidirectional form of a SynchronousQueue. Exchangers may be useful in applications such as genetic algorithms and pipeline designs.
在以上的描述中,有几个要点:
此类提供对外的操作是同步的;
用于成对出现的线程之间交换数据;
可以视作双向的同步队列;
可应用于基因算法、流水线设计等场景。
接着看api文档,这个类提供对外的接口非常简洁,一个无参构造函数,两个重载的范型exchange方法:
public V exchange(V x) throws InterruptedException
public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
2 Exchanger 实例
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executor.execute(new Runnable() {
String data = "data1";
@Override
public void run() {
doExchangeWork(data, exchanger);
}
});
executor.execute(new Runnable() {
String data = "data2";
@Override
public void run() {
doExchangeWork(data, exchanger);
}
});
executor.shutdown();
}
private static void doExchangeWork(String data, Exchanger exchanger) {
try {
System.out.println(Thread.currentThread().getName() + "正在把数据 " + data + " 交换出去");
Thread.sleep((long) (Math.random() * 1000));
String exchangeData = (String) exchanger.exchange(data);
System.out.println(Thread.currentThread().getName() + "交换得到数据 " + exchangeData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool-1-thread-1正在把数据 data1 交换出去
pool-1-thread-2正在把数据 data2 交换出去
pool-1-thread-2交换得到数据 data1
pool-1-thread-1交换得到数据 data2
当线程A调用Exchange
对象的exchange()
方法后,他会陷入阻塞状态,直到线程B也调用了exchange()
方法,然后以线程安全的方式交换数据,之后线程A和B继续运行。
exchange等待超时
public class ExchangerTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executor.execute(new Runnable() {
String data = "data1";
@Override
public void run() {
doExchangeWork(data, exchanger);
}
});
executor.execute(new Runnable() {
String data = "data2";
@Override
public void run() {
try {
Thread.sleep((long) (3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
doExchangeWork(data, exchanger);
}
});
executor.shutdown();
}
private static void doExchangeWork(String data, Exchanger exchanger) {
try {
System.out.println(Thread.currentThread().getName() + "正在把数据 " + data + " 交换出去");
//远小于3秒抛出异常
String exchangeData = (String) exchanger.exchange(data,1, TimeUnit.SECONDS);
System.out.println(Thread.currentThread().getName() + "交换得到数据 " + exchangeData);
} catch ( TimeoutException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
pool-1-thread-1正在把数据 data1 交换出去
java.util.concurrent.TimeoutException
at java.util.concurrent.Exchanger.exchange(Exchanger.java:626)
at ExchangerTest.doExchangeWork(ExchangerTest.java:37)
at ExchangerTest.access$000(ExchangerTest.java:3)
at ExchangerTest$1.run(ExchangerTest.java:12)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
pool-1-thread-2正在把数据 data2 交换出去
java.util.concurrent.TimeoutException
at java.util.concurrent.Exchanger.exchange(Exchanger.java:626)
at ExchangerTest.doExchangeWork(ExchangerTest.java:37)
at ExchangerTest.access$000(ExchangerTest.java:3)
at ExchangerTest$2.run(ExchangerTest.java:26)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
实战场景:
设计一个定时任务,每日凌晨执行。在定时任务中启动两个线程,一个线程负责对业务明细表(xxx_info)进行查询统计,把统计的结果放置在内存缓冲区,另一个线程负责读取缓冲区中的统计结果并插入到业务统计表(xxx_statistics)中。
亲,这样的场景是不是听起来很有感觉?没错!两个线程在内存中批量交换数据,这个事情我们可以使用Exchanger去做!
3 实现原理
Exchanger
(交换者)是一个用于线程间协作的工具类。Exchanger
用于进行线程间的数据交换。它提供一个同步点,在这个同步点两个线程可以交换彼此的数据。这两个线程通过exchange
方法交换数据, 如果第一个线程先执行exchange
方法,它会一直等待第二个线程也执行exchange
,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。因此使用Exchanger
的重点是成对的线程使用exchange()方法,当有一对线程达到了同步点,就会进行交换数据。因此该工具类的线程对象是成对的。
Exchanger类提供了两个方法,String exchange(V x):用
于交换,启动交换并等待另一个线程调用exchange
;String
exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange
,并且设置最大等待时间,当等待时间超过timeout
便停止等待。
来源:https://juejin.cn/post/7021034812295102501


猜你喜欢
- 本文实例讲述了Java基于Runtime调用外部程序出现阻塞的解决方法, 是一个很实用的技巧。分享给大家供大家参考。具体分析如下:有时候在j
- 前言最近在学习Android开发,发现确实有太多东西需要去整理,去学习。慢慢来吧,任何东东的深入学习都是不简单的。今天稍微整理下Spanna
- Springboot 在普通类型注入Service或mapper最近遇到一个难题(大佬可能感觉这太简单了把),对于我这样的小白来说,确实有些
- Java进阶之FileUpload完成上传的实例 FileUpload是Apache commons下面
- 本文实例为大家分享了C++实现连连看游戏的具体代码,供大家参考,具体内容如下这个项目还是挺不错的,运行后也比较有意思,可以看看。#inclu
- strftime函数主要用于时间格式化,它的函数原型如下:size_t __cdecl strftime(char * __restrict
- 使用wait()与notify()实现线程间协作1. wait()与notify()/notifyAll()调用sleep()和yield(
- 概述事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性。Spring Framework对事务管理提供了一致的
- 目录前言:对文章出现的一些名词进行解释一、插入排序1.基本思想2.直接插入排序3.希尔排序(缩小增量排序)二、选择排序1.基本思想2.直接选
- 简介机器可以读,人为什么不能读?只要我们掌握java class文件的密码表,我们可以把二进制转成十六进制,将十六进制和我们的密码表进行对比
- MyBatis Example And与Or混合使用(条件1 and 条件2) or ( 条件3 and 条件4)  
- 体验了一下美团外卖的底部导航栏,感觉动画很流畅,分割线被顶起,还有图标的动画,可能用的lottie,觉得分割线被顶起可以自己写动画,所以试着
- 大部分的 Java 软件开发都会使用到各种不同的库。近日我们从一万个开源的 Java 项目中进行分析,从中提取出最常用的 Java 类,这些
- 默认情况下Spring Boot使用了内嵌的Tomcat服务器,项目最终被打成jar包运行,每个jar包可以被看作一个独立的Web服务器。传
- 本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI。1先看效果图: 第一:chat.xml设计 &l
- 说明使用工具:brew caskbrew cask是一个用命令行管理Mac下应用的工具,提供了自动安装和卸载功能,能够自动从官网上下载并安装
- Java中的引用类型有哪几种?Java中的引用类型分成 强引用 , 软引用 , 弱引用 , 虚引用 。1、强引用没有引用指向这个对象,垃圾回
- 经常进行图形界面设计的编程者肯定知道,控件在设计的时候才能随意拖动,运行的时候就不能拖动了.你肯定会问:运行时能随意拖动控件吗?答案是肯定的
- layout布局<RelativeLayout xmlns:android="http://schemas.android.
- 在上文实现的带小圆球的图片滑动的通用性较好,但是较复杂。现在也是利用 ViewPager ,但是却没有利用 ShapeDrawable 来实