转载请注明源出处: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的更多相关文章

  1. Java从零开始学二十四(集合工具类Collections)

    一.Collections简介 在集合的应用开发中,集合的若干接口和若干个子类是最最常使用的,但是在JDK中提供了一种集合操作的工具类 —— Collections,可以直接通过此类方便的操作集合 二 ...

  2. Java开发学习(二十四)----SpringMVC设置请求映射路径

    一.环境准备 创建一个Web的Maven项目 参考Java开发学习(二十三)----SpringMVC入门案例.工作流程解析及设置bean加载控制中环境准备 pom.xml添加Spring依赖 < ...

  3. Java之集合(二十六)ConcurrentSkipListMap

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7542578.html 1.前言 一个可伸缩的并发实现,这个map实现了排序功能,默认使用的是对象自身的compa ...

  4. Java学习笔记二十四:Java中的Object类

    Java中的Object类 一:什么是Object类: Object类是所有类的父类,相当于所有类的老祖宗,如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类默认继承Object ...

  5. 夯实Java基础(二十四)——Java8新特征之Optional类

    1.概述 对于Java程序员来说,到目前为止出现次数最多的应该是NullpointException,它是导致Java应用程序失败的最常见原因.之前处理空指针我们必须先通过条件先去判断,然后再确认是否 ...

  6. Java之集合(二十二)PriorityBlockingQueue

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7510799.html 1.前言 本章介绍阻塞队列PriorityBlockingQueue.这是一个无界有序的阻 ...

  7. Java基础(二十四)Java IO(1)输入/输出流

    在Java API中,可以从其中读入一个字节序列的对象称作输入流,而可以向其中写入一个字节序列的对象称为输出流. 输入流的指向称为源,程序从指向源的输入流中读取数据. 输出流的指向是字节要去的目的地, ...

  8. java 面向对象(二十四):interface:接口

    interface:接口1.使用说明: 1.接口使用interface来定义 * 2.Java中,接口和类是并列的两个结构 * 3.如何定义接口:定义接口中的成员 * * 3.1 JDK7及以前:只能 ...

  9. Java之集合(二十五)ConcurrentHashMap

    转载请注明源出处:http://www.cnblogs.com/lighten/p/7520808.html 1.前言 本章介绍使用的最频繁的并发集合类之一ConcurrentHashMap,之前介绍 ...

随机推荐

  1. HDU 3177 Crixalis's Equipment (贪心,差值)

    题意:判断 n 件物品是否可以搬进洞里,每件物品有实际体积A和移动时的额外体积 B . 析:第一反应就是贪心,一想是不是按B从大到小,然后一想,不对,比如体积是20,第一个 是A=11, B=19.第 ...

  2. HDU 1009 FatMouse' Trade (贪心算法)

    题意:就是老鼠要用猫粮换粮食,第i个房间一些东西,要用东西去换,可以不全换.问给定的猫粮最多能换多少粮食. 析:贪心算法.我们先算出来每个房间物品的平均价格是多少,肯定越低越好,并且如果能全换就全换, ...

  3. MATLAB矩阵的一些用法

    1.怎样去提取和修改矩阵中的一个元素. (1)创建一个矩阵 >> A=[1,2,3,4;5,6,7,8;9,10,11,12;13,14,15,16]A =     1     2     ...

  4. tomcat自动关闭了。

    测试方法: 1.狂点抽取大量数据的接口 结果: jvm里面的现成崩溃.导致tomcat错误. 思路: 最近发现tomcat老是自动关闭,开始也发现了,不过没放在心上,直到今天,请求一提交到服务器,to ...

  5. 20155216 2016-2017-2 《Java程序设计》第六周学习总结

    20155216 2016-2017-2 <Java程序设计>第六周学习总结 教材学习内容总结 流与IO 将数据从来源中取出,可以使用输入串流:将数据写入目的地,可以使用输出串流,串流是有 ...

  6. day03(接口,多态)

    接口:            概念:是功能的集合,可以当做引用数据类型的一种.比抽象类更加抽象. 接口的成员:               成员变量:必须使用final修饰 默认被 public &a ...

  7. 用VS2010编译python2.7的源码

    1.下载python2.7的源码,解压缩如下目录 2. 网上有些教程说从PCbuild目录中进入,打开sln文件,但是我这样做是不能用vs2010打开的, 并且也尝试了用VS2013打开sln,但是是 ...

  8. SoC FPGA开发板的FPGA配置数据下载和固化

    小梅哥编写,未经许可,严禁用于任何商业用途 2018年7月2日星期一  soc fpga的烧写和固化方式与传统的纯fpga固化方式即存在形式上的相同,也存在细节上的差异,特整理此文. AC501-So ...

  9. 理解maven项目的pom.xml文件中,<scope>标签的作用——作用域以及依赖传递

    问题介绍: 在maven项目中,最关键的就是pom.xml这个文件,这个文件是用来导入maven项目依赖的jar包以及一些插件等. 在这个文件中导入jar包使用的标签是<dependency&g ...

  10. delphi TStringList 用法详解

    转自: http://blog.163.com/you888@188/blog/static/67239619201472365642633/ delphi TStringList 用法详解 2014 ...