ConcurrentLinkedDeque源码解析
Java的并发包JUC大概包括五大部分,分别是原子操作类、锁机制、并发集合、并发框架和并发集合。在之前大概的阅读了前两者的实现过程。接下来准备读一读并发集合相关的实现。之前我们研究原子类的时候基本的实现是借助于valative和CAS来实现的,那么对于集合又如何让其在高并发的时候能够高效率的存储和获取,为什么这么说,难道JUC做的那么多并发集合就是为了解决这个问题?因为之前我们我们在锁的那块说过,如果加锁之后副本代码只能择一进行运行,所以显然并发集合肯定不是咋理解的那样,准确的说,并发集合是用来在并发量高的是。最基本是当一个线程修改或者删除了集合中的数据。其他线程可能获取的值变为脏数据。所以这块的并发集合就是要解决高并发情况下的存储和获取,以至于在高并发情况下能够尽量提升系统性能。
ConcurrentLinkedDeque是一个高并发链表,从上边的截图中可以看到。ConcurrentLinkedDeque的实现是采用Unsafe来实现的,其中包括了head和tail,而head和tail是长整形的类型。那么我们大概可以猜测出该类对链表的节点的操作是采用的Unsafe来操作的,而操作的参数就是head和tail的偏移量。如果采用Unsafe操作,那么我们需要看一下这个链表的节点定义,按照之前的学习,那么node应该是线程可见的。
在Node节点中定义中,我们看到链表的一般结构。但是我们发现在链表中我们依然看到了偏移量的定义,按照我们之前的想法,我觉得用一个偏移量就可以了,就是记录整个链表的的首和尾.
但是仔细想确实有问题,如果我们要操作中间一个节点的后置或者前置,那要去通过首和尾的偏移量去计算吗,如何中间某个节点足够大,那么根本没办法知道节点的开始和结束的位置,而且我们操作是基于Unsafe的,所以地址偏移量很重要。需要在每个节点记录一次。除此之外,node的每个节点提供对节点的CAS操作的方法。其实现的过程和我们在原子类中的CAS分析一样。这里不再赘述。
在剩余的内部类中,我们看到了迭代器。这里的迭代器就相当于ConcurrentLinkedDeque的专用迭代器。这里我们先不看迭代器,我们先看对链表操作的一般步骤就是增删改查的操作。
private void linkFirst(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
restartFromHead:
for (;;)
for (Node<E> h = head, p = h, q;;) {
if ((q = p.prev) != null &&
(q = (p = q).prev) != null)
// Check for head updates every other hop.
// If p == q, we are sure to follow head instead.
p = (h != (h = head)) ? h : q;
else if (p.next == p) // PREV_TERMINATOR
continue restartFromHead;
else {
// p is first node
newNode.lazySetNext(p); // CAS piggyback
if (p.casPrev(null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this deque,
// and for newNode to become "live".
if (p != h) // hop two nodes at a time
casHead(h, newNode); // Failure is OK.
return;
}
// Lost CAS race to another thread; re-read prev
}
}
}
在将元素添加到头部节点的时候,我们一般的操作是将头部的指针指向该节点,然后让该节点的next指向原来的头节点的next节点。大概的过程是这样的。但是上边的操作的都是基于Unsafe的,上边的代码是先找到head,而多线程情况下可能head一直在变的缘故吧,这里拿到head之后好像是继续往前走,直到找到真的头节点,然后才进行casPrev操作,成功之后才进行头节点的设置。如果设置失败,也就是当前线程找到的头已经被其他线程修改了,那么就自旋,继续向前找。直到设置成功。通过linkFirst的阅读,那么liskLast的操作应该也差不多
public E pollFirst() {
for (Node<E> p = first(); p != null; p = succ(p)) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
unlink(p);
return item;
}
}
return null;
}
在pollFirst中,首先拿到头接点,然后去判断是否是头节点,如果是的话就将头节点释放,然后将头简单返回。那么pollLast方法也差不多。
在push、pop提供了栈的用法。toArrayList也没有什么要写的了,都是常规操作。看了看,好像没啥了。好了今天的分析就到这里了。晚安!
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- js表单验证工具包
- 手把手教你,嘴对嘴传达------Nginx网站服务(访问状态统计,基于域名,端口,IP的虚拟web主机访问)
- lib-flexible引入到Vue做移动端rem布局
- 微信小程序引入VantWeapp开发
- 手把手教你,嘴对嘴传达------源码编译LNMP部署及应用 , 手动搭建discuz论坛
- 微信小程序封装api接口
- 手把手教你,嘴对嘴传达------Nginx常规的优化(隐藏版本号,缓存时间,日志切割,网页压缩,防盗链优化)
- Vue Router 实现多种页面跳转
- Vue实现输入框自动聚焦
- 手把手教你,嘴对嘴传达------Apache --ab测试
- Css实现内容溢出添加横向滚动条
- 手把手教你,嘴对嘴传达------深入介绍Nginx的rewrite模块(理论加实验)
- jQuery实现点击图片弹出视频并自动播放
- 机器学习之决策树一-ID3原理与代码实现
- jQuery点击返回顶部