50 通过Exchanger模拟咖啡续杯
Diego38 16 1

1. 前言

本节我们学习最后一个工具类—Exchanger,Exchanger的面试和工作中运用较少,仅作为了解即可。

有兴趣可以研究下实现,总体来说代码实现相对复杂,也无需深究内部原理。

2. Exchanger介绍

Exchanger也被称作交换器,是用于两个线程在同一时刻交换数据,一个线程如果发起exchange,等待另外一个线程发起exchange,完成交换,否则就会等待。

Exchanger好比一个转换器,举个例子,A线程喝水将空水杯提交到exchanger,待到有B线程服务员将倒满水的另一杯水提交到同一个exchanger,就可以完成空水杯和满水杯的两个线程间的互换。

Exchanger和前面我们学习过的SynchronousQueue类似,SynchronousQueue内部不存放任何元素,当一个线程来入队元素时,另外一个线程要同时去取才能唤醒入队线程,也就是经过配对,完成数据交换。

Exchanger不同于SynchrousQueue之处是,后者要求进行入队列操作的线程与出队列操作的线程进行配对,而前者只要求两个调用exchanger.exchange(e)的线程配对互换元素,所以对于类似生产消费场景exchanger,SynchrousQueue只适合于两个线程的情况,并且一个线程负责生产,令一个线程负责消费。

也就是说,Exchanger并不严格区分线程角色,仅仅是交换数据,SynchrousQueue传递一份数据从一个线程到另一个线程,而Exchanger是互换数据,其实是操作的是两份数据。

接下来我们通过代码来了解下Exchanger

3. 通过Exchanger模拟咖啡续杯

我们模拟两个线程,线程1传递空杯子,线程2传递续满的咖啡,两个线程通过Exchanger交换数据,代码如下:

public class ExchangerTest {

    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        //线程1 持续得到续满的咖啡
        fixedThreadPool.submit(() -> {
            while (true) {
                String name = "空杯子";
                try {
                    String exchange = exchanger.exchange(name);
                    System.out.println("得到的满的咖啡:" + exchange);

                } catch (InterruptedException e) {}
            }
        });
        //线程2 持续得到空杯子
        fixedThreadPool.submit(() -> {
            while (true) {
                String name = "续满咖啡";
                try {
                    String exchange = exchanger.exchange(name);
                    System.out.println("得到的空的杯子:" + exchange);

                } catch (InterruptedException e) {}
            }
        });
    }
}

输出如下:

得到的满的咖啡:续满咖啡
得到的空的杯子:空杯子
得到的空的杯子:空杯子
得到的满的咖啡:续满咖啡
得到的满的咖啡:续满咖啡
得到的空的杯子:空杯子
得到的空的杯子:空杯子

我们可以注释掉线程2,再观察下效果,会出现线程1执行Exchange一直等待的情况,这点和SynchrousQueue比较类似。

4. 总结

Exchanger用于数据交换,需要两个线程同时执行exchange方法进行配对,底层的实现既不是AQS也不是LOCK,而是基于CAS自己实现了一套交换机制。

预览图
评论区

索引目录