《java.util.concurrent 包源码阅读》18 Exchanger
Exchanger可以看做双向数据传输的SynchronousQueue,即没有生产者和消费者之分,任意两个线程都可以交换数据。
在JDK5中Exchanger被设计成一个容量为1的容器,存放一个等待线程,直到有另外线程到来就会发生数据交换,然后清空容器,等到下一个到来的线程。
从JDK6开始,Exchanger用了类似ConcurrentMap的分段思想,提供了多个slot,增加了并发执行时的吞吐量。
Exchanger不存在公平不公平的模式,因为没有排队的情况发生,只要有两个线程就可以发生数据交换。
直接看核心方法:
private Object doExchange(Object item, boolean timed, long nanos) {
Node me = new Node(item);
// index是线程ID的hash值映射到0到max之间的一个值
// 一般情况下max为0,这样线程交换数据只会使用第一个slot,
// 即index是0,而max不为0情况请看下面的循环
int index = hashIndex(); // CAS操作失败的次数
int fails = 0; for (;;) {
// 当前slot中存储的对象,也就是Node
Object y;
Slot slot = arena[index];
// 延迟加载,即只有当slot为null时才创建一个slot
// 延迟加载后重新循环一次
if (slot == null)
createSlot(index);
// slot中有数据,也就意味着有线程在等待交换数据
// 这时可以尝试用CAS重置slot(把slot存储的对象设为null)
// 用slot中存储的对象和当前线程进行数据交换
// 如果交换成功就通知原先等待的线程
else if ((y = slot.get()) != null &&
slot.compareAndSet(y, null)) {
Node you = (Node)y;
if (you.compareAndSet(null, item)) {
LockSupport.unpark(you.waiter);
return you.item;
}
// 如果slot存储的对象已经被重置为null,但是数据交换失败了
// 这时就意味着这个等待的线程的交换请求被取消了
// 在分析wait类型的方法代码时会看到如何处理这种情况
}
// 如果slot中没有存储对象,那么首先尝试把当前线程存储到slot中
// 如果存储失败了,就重新循环
else if (y == null &&
slot.compareAndSet(null, me)) {
// index为0意味着仅仅有当前线程在等待交换数据,因此直接等待即可
if (index == 0)
return timed ?
awaitNanos(me, slot, nanos) :
await(me, slot);
// 所谓的spin wait:就是固定次数循环,每次计数减一
// 对于单核系统来说,spin wait是不做的,因为单核
// 做wait时需要占用CPU,其他线程是无法使用CPU,因此这样
// 的等待毫无意义。而多核系统中spin值为2000,也就是会做
// 2000次循环。
// 如果循环完成后依然没有得到交换的数据,那么会返回一个
// CANCEL对象表示请求依旧被取消,并且把Node从slot中清除
Object v = spinWait(me, slot);
if (v != CANCEL)
return v;
// 如果取消了,就新建一个Node取消原先取消的Node用于下次循环
me = new Node(item);
int m = max.get();
// index除2,缩小slot的范围
// 同时如果m过大,减小m
if (m > (index >>>= 1))
max.compareAndSet(m, m - 1);
}
// 允许CAS失败两次,因为两个else if中都有CAS,因此这里
// 允许两个else if的CAS操作都失败过
else if (++fails > 1) {
int m = max.get();
// 失败超过3次,增大m,并且从m处重新索引
if (fails > 3 && m < FULL && max.compareAndSet(m, m + 1))
index = m + 1;
// 当index小于0,回到m,重新循环
else if (--index < 0)
index = m;
}
}
}
这篇文章关于索引index这块弄得不是很清楚,后续会继续研究,及时更新。
《java.util.concurrent 包源码阅读》18 Exchanger的更多相关文章
- 《java.util.concurrent 包源码阅读》 结束语
<java.util.concurrent 包源码阅读>系列文章已经全部写完了.开始的几篇文章是根据自己的读书笔记整理出来的(当时只阅读了部分的源代码),后面的大部分都是一边读源代码,一边 ...
- 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分
这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...
- 《java.util.concurrent 包源码阅读》02 关于java.util.concurrent.atomic包
Aomic数据类型有四种类型:AomicBoolean, AomicInteger, AomicLong, 和AomicReferrence(针对Object的)以及它们的数组类型, 还有一个特殊的A ...
- 《java.util.concurrent 包源码阅读》04 ConcurrentMap
Java集合框架中的Map类型的数据结构是非线程安全,在多线程环境中使用时需要手动进行线程同步.因此在java.util.concurrent包中提供了一个线程安全版本的Map类型数据结构:Concu ...
- 《java.util.concurrent 包源码阅读》17 信号量 Semaphore
学过操作系统的朋友都知道信号量,在java.util.concurrent包中也有一个关于信号量的实现:Semaphore. 从代码实现的角度来说,信号量与锁很类似,可以看成是一个有限的共享锁,即只能 ...
- 《java.util.concurrent 包源码阅读》06 ArrayBlockingQueue
对于BlockingQueue的具体实现,主要关注的有两点:线程安全的实现和阻塞操作的实现.所以分析ArrayBlockingQueue也是基于这两点. 对于线程安全来说,所有的添加元素的方法和拿走元 ...
- 《java.util.concurrent 包源码阅读》09 线程池系列之介绍篇
concurrent包中Executor接口的主要类的关系图如下: Executor接口非常单一,就是执行一个Runnable的命令. public interface Executor { void ...
- 《java.util.concurrent 包源码阅读》05 BlockingQueue
想必大家都很熟悉生产者-消费者队列,生产者负责添加元素到队列,如果队列已满则会进入阻塞状态直到有消费者拿走元素.相反,消费者负责从队列中拿走元素,如果队列为空则会进入阻塞状态直到有生产者添加元素到队列 ...
- 《java.util.concurrent 包源码阅读》10 线程池系列之AbstractExecutorService
AbstractExecutorService对ExecutorService的执行任务类型的方法提供了一个默认实现.这些方法包括submit,invokeAny和InvokeAll. 注意的是来自E ...
随机推荐
- win10 uwp 保存用户选择文件夹
如果我们每次把临时处理的文件保存,都要让用户选择一次,用户会不会觉得uwp垃圾?如果我们每次打开应用,都从某个文件读取,而这个文件不在应用目录和已知的目录,那么每次都需要用户选择,用户会不会觉得uwp ...
- C陷阱和缺陷学习笔记
这段时间把<C陷阱和缺陷>看了,没时间自己写总结.就转一下别人的学习笔记吧http://bbs.chinaunix.net/thread-749888-1-1.html Chapter 1 ...
- java选择结构
/**选择结构*/ public class Demo06{ public static void main(String []args){ // if...else...型 String name ...
- windows消息简单应用实例
//基本定义 internal class MyMessager : IMessageFilter { public bool PreFilterMessage(ref Message m) { // ...
- LeetCode 74. Search a 2D Matrix(搜索二维矩阵)
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...
- Android Studio常见问题解决
1.Error:Execution failed for task ':XXXX:processDebugManifest'. > Manifest merger failed with mul ...
- ubuntu中python3.4安装pip
这两天碰到在ubuntu中安装pip的问题. 第一种方法 用百度搜索了一下,基本上都是这个命令: sudo apt-get install python3-pip 但是,用这条命令下载速度特别慢. 第 ...
- 《mysql必知必会》读书笔记--安全管理及数据库维护
安全管理 mysql自带的mysql数据库中的user表可查看用户所有资料 创建用户帐号 CREATE USER ben IDENTIFIED BY 'p@$$wOrd' 重命名用户帐号 RENAME ...
- Tempter of the Bone
Problem Description The doggie found a bone in an ancient maze, which fascinated him a lot. However, ...
- Python pip 下载速度慢? Windows 设置 国内源,用 阿里云 国内镜像 加速
pip 提供了对 Python 包的查找.下载.安装.卸载的功能,是非常方便的 Python 包管理工具.但是,令人苦恼的是 pip 在国内的下载速度非常慢,速度常常只有每秒几十 K,甚至才几 K,小 ...