Java并发编程——Exchanger

1. 简介1.1 概述

前面已经介绍SyclicBarrier、CountDownLatch、Semaphore三个并发编程中的工具类,还剩下最后一个Exchanger。Exchanger(交换者)是一个用于线程间数据交换协作的工具类。它提供一个同步点,在这个同步点多个线程间两两之间线程可以交换彼此的数据。这两个线程通过exchange方法交换数据, 如果第一个线程先执行exchange方法,它会一直等待第二个线程也执行exchange方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

1.2 Api接口

查阅Exchanger的源码,可以发现给我们提供的公共方法只有三个。

Exchanger():无参构造方法

exchange(V):exchange方法用于交互数据V

exchange(V,long,TimeUnit):延迟一定时间交换数据

Exchanger源码简洁,但是它的设计思想还是比较复杂的。CyclicBarrier、CountDownLatch通过借助AbstractQueuedSynchronized的state字段进行“临界点”的标识,Exchanger是如何实现"临界点"的判断呢?

1.3 实例演示

创建连个线程,进行内部数据交换。

运行结果:

在两个线程中分别调用exchange方法进行数据的交换,当第二个线程调用exchange方法的时候,数据进行交换。

2. 源码解析

Exchanger是一个用于成对线程之间交换数据的同步器。主要用于遗传算法和管道通讯等两两交换比对的场景。

内部结构:

一个无参的构造方法,用于创建一个Exchanger实例。内部结构很清晰,首先内部包含一个Slot数组,默认容量是32,用来避免以一些竞争,有点类似于ConcurrentHashMap的策略;其次,交换数据的场所就是Slot,它本身进行了cache line填充,避免了伪共享问题;最后,每个要进行数据交换的线程在内部会用一个Node来表示。

关键技术点1:CacheLine填充

在上面的代码中,Slot其实就是一个AtomicReference,其里面的q0, q1,..qd那些变量,都是多余的,不用的。那为什么要添加这些多余的变量呢?

是为了让不同的Slot不要落在cpu的同一个CacheLine里面。因为cpu从内存读取数据的时候,不是一个字节一个字节的读,而是按块读取,这里的块也就是“CacheLine”,一般一个CacheLine大小是64Byte。

保证一个Slot的大小 >= 64Byte,这样更改一个Slot,就不会导致另外一个Slot的cpu cache失效,从而提高性能。

通过前面示例,我们知道Exchanger类最核心的是exchange方法。

如果当前线程没有被interrupted,则调用doExchange方法进行数据交换。

所以,exchange的思路是:

根据每个线程的thread id, hash计算出自己所在的slot index;

如果运气好,这个slot被人占着(slot里面有node),并且有人正在等待交换,那就和它进行交换;

slot为空的(slot里面没有node),自己占着,等人交换。没人交换,向前挪个位置,把当前slot里面内容取消,index减半,再看有没有交换;

挪到0这个位置,还没有人交互,那就阻塞,一直等着。别的线程,也会一直挪动,直到0这个位置。

所以0这个位置,是一个交易的“终结点”位置!别的位置上找不到人交易,最后都会到0这个位置。

至此,整个逻辑梳理完毕。

参考阅读

http://blog.csdn.net/chunlongyu/article/details/52504895

http://brokendreams.iteye.com/blog/2253956

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180624G0MKLS00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券