Java之集合(十七)ConcurrentLinkedQueue

  转载请注明源出处:http://www.cnblogs.com/lighten/p/7491057.html

1.前言

  ConcurrentLinkedQueue是一个无界的线程安全队列,遵循FIFO先进先出的原则。头元素就是队列中存在最长时间的一个元素,即插入元素都是尾插入。检索是从头元素开始,不允许存在空元素。迭代器是弱一致性的,不会抛出并发异常。注意:size方法不是一个线性时间操作,这是由于队列的特点,需要遍历统计,如果遍历过程中有修改,就可能得到错误的结果(如果不是需要具体值,一般使用isEmpty来判断,比size>0快)。此外,bulk操作,即addAll,removeAll,retainAll,containsAll和equals,toArray这些方法不保证原子性。

2.ConcurrentLinkedQueue

2.1 数据结构

  数据结构非常简单,就只有一个head和一个tail的Node类型头尾节点,所以其size方法是遍历出来的,由于没有加锁,所以该size实际上可能不正确。

  Node节点也十分的简单,只有一个值和下一个节点的引用。不过其替换都是通过CAS操作完成的。

  头尾结点初始化为一个值为null的结点。

2.2 基本操作

  添加一个元素:

  第一时间就将tail节点赋值给t,t就是当前时刻的一个快照。先从单线程开始理解,先插入一个元素1,这个时候q==null,设置p的next为元素1,注意此时tail节点并没有被改变。再插入一个元素2,由于tail没有改变,所以p不为null,p = q = 元素1进入下一个循环,元素1的后面节点为null,所以元素2被设置为元素1的next,此时p!=t,重置元素尾。由这个过程可以看出,tail并不是第一时间进行改变,而是要隔一个以上进行变化。多线程下就存在插队的情况了,被插队了还需要判断是否被插入了多个队,尾结点被改变了就被插入两个并改变了,就需要以最新的尾结点进行循环尝试插入,否则就选择后面一个节点。p==q的情况发生在poll操作的时候:假设队列中只有二个节点,但是在重新定义tail节点的时候被其它线程抢占了,此时head和tail都没有改变。A线程再插入一个元素,此时t=tail,p=tail,在q赋值的时候暂停了,这里要明白head与tail是相等的,是为null的结点。B线程执行poll操作,poll第一次头结点元素是null,不是我们需要的,再次循环,找到元素1。元素1更新头结点的时候,队列的head变成了元素2,但是head的next(null节点)变成了head自己。这个时候回到A线程,tail=head的,q=tail.next=tail=p,就是这样产生的p等于q。如果还没明白参考文章:这里

  取出一个元素:

  这里在上面也描述了一些。poll设置头结点也是要跳过一个才会设置头结点。理解了这个,这段代码也就没什么了,也就是通过CAS进行保证线程安全。这里p==q的情况就更简单了,就是offer的情况,不过是同时有两个线程poll而已,B线程成功了,C线程失败了就会产生p==q的情况。

  其它的方法就不再进行描述了。该集合使用的时候还是需要注意的,使用不当效率反而会下降。再给一篇参考文章:这里

时间: 2024-11-03 22:45:37

Java之集合(十七)ConcurrentLinkedQueue的相关文章

关于java同步包中ConcurrentLinkedQueue类的深入分析与理解

一,官方描述 一个基于连接节点的无界线程安全队列.这个队列的顺序是先进先出.队列头部的元素是留在队列中时间最长的,队列尾部的元素是留在队列中时间最短的.新元素被插入到元素的尾部,队列从队列的头部检索元素.当许多线程共享访问同一个集合时,这个类是不二选择.这个队列不允许有null元素. 这个实现基于一种被描述为简单,快速,实用的非阻塞和阻塞公布队列算法而提供的一种有效的空闲等待算法. 注意,不像大多数集合,size方法的操作不是常量时间的,由于是异步队列,决定了元素的数量需要遍历真个元素集. 这个

Java并非集合

Java并发集合 并发集合实现1 JDK1.5的出现,对于集合并发编程来说,java developer有了更多的选择.不过,在JDK1.5之前,Java也还是提供了一些解决方案. (1)最为简单直接的就是在程序中我们自己对共享变量进行加锁.不过,缺点也显而易见,手动实现线程安全间接增加了程序的复杂度,以及代码出错的概率---例如:线程死锁的产生: (2)我们还可以使用Java集合框架中的Vector.Hashtable实现类,这两个类都是线程安全的.不过,Java已不提倡使用. (3)此外,我

六:Java之集合

集合包含的内容很多,我发现一个很好的博客,感觉自己都没有写的必要了! 链接献上  Java集合 六:Java之集合,布布扣,bubuko.com

Java复习-集合

Java的集合大致分为以下几类 map set list queue set代表无序,不可重复的集合 list代表有序,重复的集合 map代表了映射关系的集合 queue代表的是一种队列集合 从上面的图我们可以看出,访问set集合的时候我们只能根据元素本身来访问,访问list集合的时候可以直接根据元素的索引来访问,如果访问map集合中的元素,可以根据每项的key值访问元素,collection是list map queue的父接口,该接口里定义的方法,可以操作set map queue pack

java的集合框架之一

java是一套很成熟的东西,很多商用的东西都喜欢用它,用的人多,稳定.不过一般也不怎么说起它,因为太常见了,私下里说,写java应用层得就像农民工,每一处都是搭积木,根据设计师的东西如何优雅地搭好积木,当然美其名曰,论农民工搭积木的艺术修养.不难,但是东西很多,经验在里面是一个相当重要的东西.以上纯属每天扯淡,笑看即可,毕竟我目前就是个渣java程序员. java的集合框架以collection接口当作基础,这个接口定义了基本框架,包括size.hashcode.iterator.add.add

【Java】集合_学习笔记

一.集合 1.集合类也称容器类,主要负责保存.盛装其他数据. 2.集合可以保存数量不确定的数据,保存具有映射关系的数据(也称关联数组). 3.Java5后提供一些多线程安全的集合类,放在java.util.concurrrent.(utility的缩写,意为多用途的,实用的) 4.集合只能保存对象(实际是对象的引用,习惯称对象). 5.Java的集合主要由Collection和Map两个接口派生而出,Map接口的实现类用于保存关联数组,Set集合元素不可重复,通过元素本身访问,Map集合可以通过

Java基础——集合(三)——泛型、增强for、工具类

         接上篇,<Java基础--集合(二)--迭代器.Map集合> 六.泛型 (1)定义: 泛型是一种把明确类型的工作放在了创建对象或者调用方法时候才去明确的特殊的类型. (2)格式: <数据类型> ArrayList<String> aList=new ArrayList<String>(); <String> 即表示的是泛型 (3)好处:(掌握) A:解决了黄色警告线问题 B:把运行期间的转换异常给提前到了编译期间 C:优化了程序

Java中集合与数组的切换

在Java开发中常常遇见集合与数组的互相切换,如何实现呢,呵呵呵,很简单: import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; public class Test { /** * 将Set集合转换为数组 * * @author GaoHuanjie */ private static void setT

java之集合概述

集合也称容器:从大的类别分成两类:Collection和Map,也即:单列和双列列表. java编程思想中一张图说明该体系的整体结构:其中黑色着重的类是经常使用的类. 1 Collection Collection:作为单列集合的根接口.该类集合的继承体系如下: Collection分为两大类:List和Set 1)List: 特点:有序的 collection(也称为序列):列表通常允许重复的元素.       List 接口提供了特殊的迭代器,称为 ListIterator,除了允许 Ite