傻大方


首页 > 潮·科技 > >

高并发进阶 Exchanger 双方栅栏源码深度解析



按关键词阅读:

Exchanger高并发进阶 Exchanger 双方栅栏源码深度解析文章插图
简介有时我们需要对元素进行配对和交换线程的同步点 , 使用 exchange 方法返回其伙伴的对象 , 这时我们就须要使用线程类中的 Exchanger 类了 ,
简而言之 , 可以在不同线程间交换数据 。
使用入门废话少说 , 直接上代码 。
定义执行类private static class ExchangeRunnable implements Runnable {private final Exchanger exchanger;private final String data;private ExchangeRunnable(Exchanger exchanger, String data) {this.exchanger = exchanger;this.data = http://kandian.youth.cn/index/data;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() +" 正在把数据 "+ data + " 交换出去" );Thread.sleep((long) (Math.random()*1000));String data2 = exchanger.exchange(data);System.out.println(Thread.currentThread().getName() + " 交换数据到"+ data2);} catch (InterruptedException e) {e.printStackTrace();}}}我们定义一个 Runnable 类 , 会将传入的 data 已经交换 , 并打印获取到的数据 。
测试public static void main(String[] args) {ExecutorService executor = Executors.newCachedThreadPool();final Exchanger exchanger = new Exchanger<>();executor.execute(new ExchangeRunnable(exchanger, "one"));executor.execute(new ExchangeRunnable(exchanger, "two"));executor.shutdown();}我们使用线程池执行数据交换测试 , 日志如下:
pool-1-thread-1 正在把数据 one 交换出去pool-1-thread-2 正在把数据 two 交换出去pool-1-thread-1 交换数据到twopool-1-thread-2 交换数据到one【高并发进阶 Exchanger 双方栅栏源码深度解析】可以看到两个线程的数据已经发生了交换 。
这么神奇 , 到底是如何实现的呢?
感兴趣的小伙伴可以一起来阅读以下源码 。
高并发进阶 Exchanger 双方栅栏源码深度解析文章插图
源码解析类定义/** * @since 1.5 * @author Doug Lea and Bill Scherer and Michael Scott * @param The type of objects that may be exchanged */public class Exchanger {}这个类是在 jdk1.5 引入的 。
算法笔记ps: 这里是作者的算法笔记 , 不出现在 doc 中 , 主要是便于大家理解 。 内容较多 , 可以跳过 。 阅读完源码后 , 结合起来看 。
概述:对于交换“槽(slot)” , 核心算法是一个参与者和一个项目(呼叫者):
for (;;) {if (slot is empty) {// offerplace item in a Node;if (can CAS slot from empty to node) {wait for release;return matching item in node;}}else if (can CAS slot from node to empty) { // releaseget the item in node;set matching item in node;release waiting thread;}// else retry on CAS failure}这是“双重数据结构”(dual data structure)的最简单形式之一-参见Scott和Scherer的DISC 04论文和

原则上 , 这很好 。
但是实际上 , 就像许多集中于单个位置的原子更新的算法一样 , 当使用同一个Exchanger的参与者多于几个时 , 它会可怕地扩展 。
因此 , 该实现改为使用消除域的形式 , 该域通过安排某些线程通常使用不同的插槽来扩展此争用 , 同时仍确保最终任何两个参与方都能够交换项目 。
也就是说 , 我们不能完全在线程之间进行分区 , 而是给线程提供竞技场索引 , 这些索引在争用情况下平均会增长 , 在缺乏争用的情况下会缩小 。
我们通过将我们仍然需要的节点定义为ThreadLocals来实现这一点 , 并在其中包括每个线程的索引和相关的簿记状态 。
(我们可以安全地重用每个线程的节点 , 而不必每次都重新创建它们 , 因为插槽在指向节点与空节点之间交替出现 , 因此不会遇到ABA问题 。 但是 , 在每次使用之间重置它们时 , 我们确实需要谨慎 。 )
实施有效的竞技场需要分配一堆空间 , 因此我们仅在检测到争用时这样做(单处理器除外 , 在单处理器上它们将无济于事 , 因此不会使用) 。
否则 , 交换使用单槽slotExchange方法 。
在争用时 , 插槽不仅必须位于不同的位置 , 而且由于位于同一高速缓存行(或更常见的是 , 相同的一致性单元) , 这些位置也不能遇到内存争用 。
因为在撰写本文时 , 尚无法确定高速缓存行的大小 , 所以我们定义了一个足以满足通用平台的值 。
此外 , 在其他地方也要格外小心 , 以避免其他错误/意外共享并增强位置 , 包括向节点添加填充(通过sun.misc.Contended) , 将“ bound”作为Exchanger字段嵌入 , 以及重新处理比较的某些 park/unpark 机制 到LockSupport版本 。
竞技场(arena)仅以一个已使用的插槽开始 。
我们通过跟踪碰撞来扩大有效竞技场的规模; 即尝试交换时失败了 。


稿源:(未知)

【傻大方】网址:http://www.shadafang.com/c/111T3142H020.html

标题:高并发进阶 Exchanger 双方栅栏源码深度解析


上一篇:预算不够想换新?千元机考虑这三款,性价比高配置强

下一篇:百元蓝牙耳机的又一选择:长续航酷狗M3Pro能量圈颈带式耳机