Java之集合(二十四)ConcurrentLinkedDeque
转载请注明源出处:http://www.cnblogs.com/lighten/p/7517454.html
1.前言
本章介绍并发队列ConcurrentLinkedDeque,这是一个非阻塞,无锁,无界 ,线程安全双端操作的队列。简单说就是ConcurrentLinkedQueue的升级版,在JDK7之后才提供。该队列也不允许空元素,而且size方法并不是常量时间,其需要遍历链表,此时并发修改链表会造成统计size不正确。同样,bulk操作和equal以及toArray方法不保证原子性。
2.ConcurrentLinkedDeque
2.1 实现原理
要明白ConcurrentLinkedDeque的实现原理,ConcurrentLinkedQueue的实现原理是基础。数据结构是对称双向链接的链表结构。通过两个方法最小化可见性写入的次数:1.通过单个CAS推进前面多个节点。2.混合对同一个内存位置可见和不可见的写入。
一个节点定义成包含前置节点pre、数据项item、后置节点next。当节点item!=null的时候视为节点还存活,当通过CAS设置item为null的时候,逻辑上该节点已经从集合中删除了。任何时候显然第一个节点的前置节点为null,其终结任何链表的从活着的结点开始的前置引用,最后一个节点同样的,其终结任何链表从活着的结点开始后面的引用。第一个节点和最后一个节点并不一定活着,而且总是能够相互到达。一个新元素原子添加是通过CAS设置前置引用或后置引用为null入队列。
活跃的结点定义为活着的结点和第一个或最后一个节点(不一定活着)。一个deque有两个节点引用:head和tail。头尾节点只是接近第一个或最后一个节点,第一个节点总是能够通过头结点的前置节点找到,最后一个节点类似。
删除节点有三个层次:
1.逻辑删除:CAS设置item为null,然后变成一个可以断开的结点
2.断开连接:删除一个非活跃节点,最终会被GC回收。断开的节点可能被迭代器无限制地访问。逻辑删除后节点还在链表上,通过第一个节点向后遍历或者通过最后一个节点向前遍历,并不保证结点已被逻辑删除,这样的结点仅能通过一个方向可达。
3.GC断开连接:使得断开连接更彻底,活跃的结点无法到达删除的结点。
当一个节点出队列,会断开所有非活跃节点的引用,自我链接是一个十分有效的方法。遍历的时候可以保证不会遍历到相同的元素,但是不保证能够看到后加入的元素。
2.2 数据结构

就是原理中所说的头尾节点和前后终止节点标志。Node的结构如下:

Node的基本方法如下:
casItem:CAS比较更新Item字段的值
lazySetNext:CAS设置next的值,不需要管原值
casNext:CAS比较更新next的值
lazySetPrev:CAS设置prev的值,不需要管原值
casPrev:CAS比较更新prev的值
2.3 基本操作
添加一个元素,都是通过LinkFirst或LinkLast完成的:

步骤如下描述:1.非空校验,创建节点。2.循环设置:头插入先找到当前头结点:1.判断是否被插队2个,是就重新找到头结点。2.判断当前头结点是否是前置终止符,是重新循环。3.p是头结点,设置新节点的next节点为p,再通过CAS设置p的前置节点,被插队失败就继续循环,成功时如果p不等于h意味着2个节点了,需要替换头结点。而后返回。这个步骤之前的队列中也介绍过类似的,并不是很难理解。唯一有疑问的就是PREV_TERMINATOR是什么时候设置的,在链表的构成时候并不存在该节点。注意,插入步骤并没有关心逻辑删除的结点,只关注了终止结点,即前终止结点的特点是其前置节点是其本身,后终止结点是后置节点是其本身。所以可能出现A-null-B的情况,当然从一端进另一端出是不会出现这种情况的,这是发生在一端入一端出。
取出一个元素:

过程很简单:先要找到第一个元素且不为null,取出该元素,该元素必须是活着的item不为null,并且本线程成功将其设置成null,才算数,断开这个节点。如果上面那边替换失败就意味着该节点已经被取出了,重新找其后继节点来处理。看似这个步骤没问题,但是仔细想想就会发现有些不对:一个队列中有AB元素,A元素出队列,1线程让A元素出队里失败被2线程抢先,找到A元素的后继节点是B,但是此刻又有3线程添加了C元素在B的前面,按照栈的后入先出的原则,C应该是要先出队列的,可是顺序却变成了B先出队列。所以这里要注意:push和pop作为堆栈使用,顺序可能会有问题(这个只是根据源码进行的推测,可能疏忽了什么导致推测有错,如果有错请在评论中留言)。这里要注意,由于整个队列可能出现A-null-B这种情况,实际上poll操作也不关心逻辑删除,遇到逻辑删除就跳过找下一个就好了,因为本身就可能是其它线程先取走了。整个顺序是不会改变的,把队列中出现的item为null的结点当做没看见就可以了,整个移除处理就是在unlink方法。
unlink方法有些长,这里直接说下其作用:如果取出节点是头结点,就移除其后面所有连续的逻辑删除节点(如A-null-null-B,unlinkA节点会定位到B最后丢弃中间的变成null-B,更新头尾和前置的终结标志,还有其它可能不再描述),如果取出的结点是尾结点,操作和头结点类似。如果是中间就找到前后非null的结点,都跳过,最后设置前后终结标志。
大体的实现过程就是这样,其它方法不描述。使用也就像一般的队列一样使用就可以了。
Java之集合(二十四)ConcurrentLinkedDeque的更多相关文章
- Java从零开始学二十四(集合工具类Collections)
一.Collections简介 在集合的应用开发中,集合的若干接口和若干个子类是最最常使用的,但是在JDK中提供了一种集合操作的工具类 —— Collections,可以直接通过此类方便的操作集合 二 ...
- Java开发学习(二十四)----SpringMVC设置请求映射路径
一.环境准备 创建一个Web的Maven项目 参考Java开发学习(二十三)----SpringMVC入门案例.工作流程解析及设置bean加载控制中环境准备 pom.xml添加Spring依赖 < ...
- Java之集合(二十六)ConcurrentSkipListMap
转载请注明源出处:http://www.cnblogs.com/lighten/p/7542578.html 1.前言 一个可伸缩的并发实现,这个map实现了排序功能,默认使用的是对象自身的compa ...
- Java学习笔记二十四:Java中的Object类
Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...
- 夯实Java基础(二十四)——Java8新特征之Optional类
1.概述 对于Java程序员来说,到目前为止出现次数最多的应该是NullpointException,它是导致Java应用程序失败的最常见原因.之前处理空指针我们必须先通过条件先去判断,然后再确认是否 ...
- Java之集合(二十二)PriorityBlockingQueue
转载请注明源出处:http://www.cnblogs.com/lighten/p/7510799.html 1.前言 本章介绍阻塞队列PriorityBlockingQueue.这是一个无界有序的阻 ...
- Java基础(二十四)Java IO(1)输入/输出流
在Java API中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称为输出流. 输入流的指向称为源,程序从指向源的输入流中读取数据. 输出流的指向是字节要去的目的地, ...
- java 面向对象(二十四):interface:接口
interface:接口1.使用说明: 1.接口使用interface来定义 * 2.Java中,接口和类是并列的两个结构 * 3.如何定义接口:定义接口中的成员 * * 3.1 JDK7及以前:只能 ...
- Java之集合(二十五)ConcurrentHashMap
转载请注明源出处:http://www.cnblogs.com/lighten/p/7520808.html 1.前言 本章介绍使用的最频繁的并发集合类之一ConcurrentHashMap,之前介绍 ...
随机推荐
- Netty学习第六节实例一步学习
NIO与传统IO对应使用的类: ServerSocketChannel相当于ServerSocket SocketChannel 相当于Socket Selector是NIO的核心类,是负责监听Ser ...
- svn错误:Can't convert string from 'UTF-8' to native encoding
如果文件名包含了中文,当执行"svn up ."遇到如下错误时: svn: Can't convert string from 'UTF-8' to native encoding ...
- 让tableView的某行移动到tableView的某位置
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:lineNumber inSection:0]; [lrcTableView selectR ...
- REST格式
首先要明确一点:REST 实际上只是一种设计风格,它并不是标准.(所以你可以看到网上一大堆的各种最佳实践,设计指南,但是没有人说设计标准).aisuhua/restful-api-design-ref ...
- SYS远程连接出错ORA-01031:Insufficient privileges
http://blog.sina.com.cn/s/blog_5f266ec50100m052.html SYS远程连接出错ORA-01031:Insufficient privileges. 现象: ...
- 拷贝构造函数——防篡改
对于普通类型的对象来说,他们之间的复制是简单的,比如: int a = 88; int b = a; 而类和普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量. #include <io ...
- supervisor配置环境变量(PATH)
app配置中增加: environment=PATH="/PATH/TO/anaconda3/bin" supervisord在linux中启动默认继承了linux的环境变量,在这 ...
- 从DevOps到Cloud Native,应用上云姿势全解锁
本文由 网易云发布. 作者:林帆 序文 伴随着IaaS.PaaS等云端基础设施技术的成熟,“应用上云”成为许多企业软件部门的心头大事.通过把传统软件系统搬到云上,一方面可以让业务方获得更多的资源灵活 ...
- Struts2乱码问题的解决办法
乱码问题的起因在于数据在web系统的各个层中间传递的时候编码不同,比如页面使用GB18030而中间层使用UTF-8.由于struts2默认使用的就是UTF-8编码,所以在页面如果使用的是其他的编码格式 ...
- Day 30 面向对象的考试题
-摘自张磊同学博客. 面向对象 1.请简述类.对象.实例化.实例这些名词的含义: #类 : 是对具有相同属性和相似行为的一类事物的抽象 #对象: 是一个具有具体属性值的类的实例化 #实例化 : 从一个 ...