Java多线程之同步工具类Exchanger

 更新时间:2021年10月28日 10:38:45   作者:冬日毛毛雨  
这篇文章主要介绍了Java多线程之同步工具类Exchanger,Exchanger 是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据,下面来看看具体内容吧

1 Exchanger 介绍

前面分别介绍了CyclicBarrierCountDownLatchSemaphore,现在介绍并发工具类中的最后一个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):用于交换,启动交换并等待另一个线程调用exchangeString exchange(V x,long timeout,TimeUnit unit):用于交换,启动交换并等待另一个线程调用exchange,并且设置最大等待时间,当等待时间超过timeout便停止等待。

到此这篇关于Java多线程之同步工具类Exchanger的文章就介绍到这了,更多相关Java多线程 Exchanger内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring事件发布监听,顺序监听,异步监听方式

    Spring事件发布监听,顺序监听,异步监听方式

    这篇文章主要介绍了Spring事件发布监听,顺序监听,异步监听方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • java String拼接json的方式实现

    java String拼接json的方式实现

    本文主要介绍了java String拼接json的方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-09-09
  • 基于Spring接口集成Caffeine+Redis两级缓存

    基于Spring接口集成Caffeine+Redis两级缓存

    这篇文章主要介绍了基于Spring接口集成Caffeine+Redis两级缓存,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • 注意Java中 new BigDecimal(double val) 的使用

    注意Java中 new BigDecimal(double val) 的使用

    这篇文章主要介绍了注意Java中 new BigDecimal(double val) 的使用,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下
    2022-07-07
  • 浅谈spring容器中bean的初始化

    浅谈spring容器中bean的初始化

    下面小编就为大家带来一篇浅谈spring容器中bean的初始化。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-06-06
  • Java实现中文字符串与unicode互转工具类

    Java实现中文字符串与unicode互转工具类

    这篇文章主要为大家详细介绍了Java实现中文字符串与unicode互转的工具类,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-04-04
  • 基于springBoot配置文件properties和yml中数组的写法

    基于springBoot配置文件properties和yml中数组的写法

    这篇文章主要介绍了springBoot配置文件properties和yml中数组的写法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java实现添加,读取和删除Excel图片的方法详解

    Java实现添加,读取和删除Excel图片的方法详解

    本文介绍在Java程序中如何添加图片到excel表格,以及如何读取、删除excel表格中已有的图片。文中的示例代码讲解详细,感兴趣的可以学习一下
    2022-05-05
  • 详解设计模式在Spring中的应用(9种)

    详解设计模式在Spring中的应用(9种)

    这篇文章主要介绍了详解设计模式在Spring中的应用(9种),详细的介绍了这9种模式在项目中的应用,具有一定的参考价值,感兴趣的可以了解一下
    2019-04-04
  • 详解Spring Cloud Zuul网关修改为短连接方法

    详解Spring Cloud Zuul网关修改为短连接方法

    本文主要介绍了详解Spring Cloud Zuul网关修改为短连接方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04

最新评论