线程间的数据共享除了定义一个共享数据然后各个线程去访问这种方式外,还可以使用Exchanger交换数据。

简单案例

首先看看Exchanger的运用,Exchanger最简单的测试代码,如下图:

对应打印的结果如下:

线程2创建对象java.lang.Object@3f6d4362

线程1创建对象java.lang.Object@c986ad7

线程2得到的类java.lang.Object@c986ad7

线程1得到的类java.lang.Object@3f6d4362

可以看出来线程2创建的对象与线程1得到的线程是同一个对象,也就是他们指向的是同一个引用。

Exchanger源码分析

Exchanger只提供了两个exchange方法,一个是只带交换的数据参数,另外一个不仅有数据参数,还有时间限制,这也从侧面说明exchange是一个阻塞线程的方法,并且可以指定阻塞时间,这里我们先分析下不带时间参数的exchange方法,源码分析主流程如下图:

左边是exchange方法的代码流程图,比较简单可以明显的看出来主要依靠两个方法slotExchange与arenaExchange方法,流程主要有两步,首先尝试从slotExchange方法获取值,如果获取为null则再从arenaExchange方法获取,当返回还是null时则抛出中断异常,否则最终返回结果

slotExchange源码分析

上图中右边的分析图是slotExchange方法的主要代码图,再分析这个方法前先说明一下Exchanger的一个内部类Node,Node有7个属性,这里先说明三个属性item(创建node线程要交换出去的值)、match(创建node线程要获取的值)、parked(创建node的线程);每个线程再执行到Exchanger方法后都会创建一个Node。

理解这个基本概念后,再来快速过一下slotExchange方法流程,方法包含两个循环,第一个循环分析如下:

首先判断Exchanger的slot是否为null,如果不为空则尝试修改slot值为null,如果修改成功则返回slot的item,并设置slot的match,并唤醒slot的线程,交换成功。如果设置失败则说明有其他线程正在修改slot,即存在竞争,这是会判断机器的CPU数,如果大于1则会初始化Exchanger的Node[] arena(这个是下个方法说明),进入下一个循环。

如果slot为null则判断arena是否为null,如果不为null则直接返回null,表示由arenaExchange去执行。

如果都不是则尝试当前线程的node的item设置为要交换的值,同时尝试设置为slot,设置成功则跳出循环,失败则进入下次循环;

第一个循环主要包含两步,如果slot不为null则尝试交换值,交换成功就返回,否则就尝试设置slot的值,设置成功就跳出循环;在cpu是多个的情况下又存在竞争的情况下会再arenaExchange去处理。

这个循环如果跳出来了则说明设置slot成功,那么当前线程只需要等待node的match不为null就行,所以第二个循环的判断条件就是node的match不为null,如果为null就挂起,当唤醒的时候再判断,直到node的match不为null则返回match的值。

分析arenaExchange方法

在slotExchange方法中会在存在竞争修改slot并且运行程序的机器的CPU大于1时会初始化Node[] arena属性,在第二次修改slot还存在竞争失败时就会返回null,这个时候就到了arenaExchange方法上场了。

arenaExchange也是一个for的无限循环,每次取出当前线程的Node来获取进行处理,这里再介绍node一个属性index,用来标记nodearena数组的位置,处理流程的简单分析如下:

首先根据node的index获取j(计算方法:index<<7+ABASE(1<<7的常量),原因后面特别说明),如果不为null则尝试修改arena的第j项为null,如果成功则返回arena[j]的node的item的值,并设置match,最后唤醒node保存的线程。

第二步如果node为null则把当前线程的node的item设置为要交换出去的值,然后尝试修改arena[j]的值为当前线程的node,如果修改成功则循环判断match的值,当不为null则返回值,否则判断线程中断等条件后则挂起线程,等唤醒再判断。

第三步也就是node不为null,但是在第一步更新失败后,会根据node的bound与Exchange的bound等对比,计算index的值,有时会增长有时会减少,然后进入下一个循环判断。

总结这三步主要目的如下,首先是尝试从arena找一个节点来交换数据,如果交换成功则唤醒对应的线程并返回交换结果,如果交换失败则更新index,再次尝试寻找并交换,如果在找到的arena[j]为null则尝试新建一个node并保存进arena,挂起线程等待唤醒

对之前的arena的j特别说明,把index扩大再作为数组的索引,原因是内存在加载数组一个元素时会把附近的也加载,而修改过后会把加载出来的设置为过期,为了避免这种浪费在数组间隔一定位置在设置值。

总结

Exchanger利用一个Node类型的属性slot实现线程间的交换,如果slot为null则初始化一个并设置item为需要交换的值,然后挂起,当其他线程进来的时候看到slot为null则取出node的item值,然后把自己要交换的值设置进match上,然后唤醒node中线程。

利用一个属性也够了,但是在并发的情况下吞吐量并不高,所以又建了一个Node的数组arena,线程在arena一个个找不为null的值,尝试修改直到修改成功,则获取node的item,再设置match并唤醒线程。如果找到的为null则自己主动new一个node并设置item,然后挂起等待唤醒。

Java程序员日常学习笔记,如理解有误欢迎各位交流讨论!

excelexportentity中设置null不显示的方法_一般人不知道的线程间数据交换Exchanger相关推荐

  1. excelexportentity中设置null不显示的方法_学习笔记-Java中的$符

    JavaEE中$符号出现在两个地方,一个是前端jQuery中的$(),一个是后端JSP中的EL表达式${}. 一.$()中放不同的东西代表不同的含义,(1)$(function(){}),表示文档加载 ...

  2. excelexportentity中设置null不显示的方法_如何在 Creator3D 中切换模型贴图,超级简单!...

    效果预览 前两天有伙伴在 QQ 上询问,如何在 Creator 3D 中切换模型贴图.Shawn 之前也没尝试过,不过根据之前 Cocos Creator 的经验以及这几天对 Creator 3D 的 ...

  3. ext中给文本框赋值的方法_大多数人不知道的Python合并字典的七种方法

    前言 Python 语言里有许多的高级特性(而且是越来越多).能够写出那些一般开发者看不懂的高级特性,就是高手,就是大神.像我这种渣渣,沉默中... 话不多说,把学到的分享给大家,仔细阅读你可以学到一 ...

  4. Ubuntu 中设置源的几种方法

    来源:http://forum.ubuntu.org.cn/viewtopic.php?t=177997 九.设置源 作者:Teliute 来源:基础教程网 Ubuntu里的许多应用程序软件包,是放在 ...

  5. word整个表格首行缩进_Word2016中设置首行缩进的方法

    缩进决定了段落到左右页边距的距离.在Word 2016中,可以使用首行缩进.左缩进.右缩进和悬挂缩进来设置段落的缩进方式.本文介绍了Word2016中设置首行缩进的方法. 默认输入的文档内容都是顶行输 ...

  6. win10计算机管理里用户,在Windows10中设置Administrators管理员用户组的方法

    Administrators是管理员用户组,它是Win10系统中权限最大的一个组,我们可以把想给予管理员权限的用户放到这个组里,之后,组内的用户可以进行所有涉及权限的操作,如修改用户密码.创建新用户. ...

  7. 在Qt中设置鼠标光标形状的方法介绍

    本文主要介绍在 Qt 中设置鼠标光标形状的方法. 说明: 本文中的应用程序是面向 Windows 操作系统的: 本文中使用的 Qt Creator 版本号为:7.0.0: 本文中使用的 Qt 版本号为 ...

  8. python程序设置_Windows系统中设置Python程序定时运行方法

    Windows系统中设置Python程序定时运行方法 一.环境 win7 + Python3.6 二.步骤 1,在Windows开始菜单中搜索"计划任务",并且点击打开" ...

  9. 向日葵远程软件设置全屏显示的方法

    描述:向日葵远程软件设置全屏显示的方法 步骤: 屏幕顶部下箭头->缩放->自适应图像即可

最新文章

  1. nginx 一个请求发给多台机器_Nginx系列二:负载均衡与反向代理
  2. vue路由传参的三种基本方式
  3. c语言选择结构程序设计报告,C语言学习与总结---第四章:选择结构程序设计
  4. mysql 与 redis 如何保证数据一致性问题 ?
  5. python 屏幕输入 读取两行_一节课带你掌握Python的输入输出
  6. netty实现gmssl_gmssl国密总结
  7. 计算机4级学那些课程,计算机四级考试科目是什么
  8. 凤凰系统基于android x x86,凤凰系统X86|Phoenix OS X86 V3.0.8.529官方版
  9. doe五步法_DOE系列--试验设计(DOE)五部曲
  10. win10与win7系统之间文件共享
  11. redis-狂神基础版
  12. 山东理工大学pta实验三四重点题目再现。
  13. 从零开发一款笔记APP——神马笔记WhatsNote
  14. 2018传智黑马前端视频教程36期视频与源码完整版
  15. h5获取当前浏览器ip和城市名称
  16. 教你一招搞定 GitHub 下载加速!
  17. 我的token鉴权机制hanhan
  18. vim工具——常用插件
  19. 互联网家装是伪命题?
  20. android申请悬浮窗代码,三行代码实现Android应用内悬浮窗,无需一切权限,适配所有ROM和厂商...

热门文章

  1. toad查看oracle的plsql包,Oracle logminer 分析redo log(TOAD与PLSQL)
  2. 前端开始学java_[Java教程]开启前端学习之路
  3. android已经点击,【已解决】android中点击其他的(如Button等)但是EditText却没有失去焦点...
  4. linux指定内核位置,ARM linux内核启动时几个关键地址
  5. php读取csv指定行_PHP快速读取CSV大文件指定行
  6. jframe和mysql登陆_刚写的一个从数据库读取账户和密码进行登陆的小程序~高手请无~...
  7. appium + python 搭建
  8. js-for (var in )遍历顺序乱了
  9. C语言-第8课 - 注释符号
  10. 字符串操作以及打印 —— 实现上传下载的进度条功能