【JUC源码解析】ConcurrentLinkedQueue
简介
ConcurrentLinkedQueue是一个基于链表结点的无界线程安全队列。
概述
队列顺序,为FIFO(first-in-first-out);队首元素,是当前排队时间最长的;队尾元素,当前排队时间最短的。新元素,从队尾插入;检索元素,从队首开始,Node的next属性保证从队首能遍历到所有的有效元素。由于此队列使用Node的next属性联接在一起,因此不允许有null元素。
队列的实现具有非阻塞,弱一致性,滞后更新等特点。
队列维护两个指针,head指针和tail指针,一开始,head和tail都指向“哑巴”结点(Node的item属性为空),新结点从尾部插入,队列向后增长,然而,tail并不总是指向尾部(队列中最后一个元素),head也并不总是指向头部(队列中第一个有效元素)。
head和tail的更新是独立的,也就是说,tail指针很可能比head指针指向更靠前的结点(当然,此结点是已删除结点,下次更新tail结点后,又跑到head结点后面或同一个),这保证了插入和删除的独立性。
hop,跳,指的是head或tail结点与第一个或最后一个结点的距离,大于等于2时更新。
新元素入队时,最后一个结点的next域为null,队列中的所有未删除结点的item域不能为null且从head都可以在O(N)时间内遍历到;
对于要删除的结点,不是将其引用直接置为null,而是将其item域先置为null(迭代器在遍历时会跳过item为null的结点);允许head和tail滞后更新,即前文提到的head/tail指针并非总是指向队列的头/尾节点。
head结点(head指针指向的结点)的next域不能指向该结点自身,tail结点的next域可以指向该结点自己。
源码解析
Node
volatile E item; // 元素内容
volatile Node<E> next; // 指向下一个结点
头结点和尾结点
private transient volatile Node<E> head; // 头结点
private transient volatile Node<E> tail; // 尾结点
入队
public boolean offer(E e) {
checkNotNull(e); // 为空,抛出异常
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t;;) { // t作为tail的快照,p指向t结点
Node<E> q = p.next; // q是p的next结点
if (q == null) { // q为null,说明p是最后一个结点,可以直接插入
if (p.casNext(null, newNode)) { // 设置新结点为p的next结点
if (p != t) // 两跳,更新tail结点
casTail(t, newNode); // 失败,表示其他线程更新成功了
return true; // 返回
}
} else if (p == q) // p结点的next域指向了它自己,此操作在更新head结点时发生,表明此结点及其之前的结点已被删除
p = (t != (t = tail)) ? t : head; // 如果tail结点已被其他线程更新,则p指向t(tail)结点,否则(tail指针指向已被删除的结点),p指向head结点,跳到头部,指向活着的结点
else
p = (p != t && t != (t = tail)) ? t : q; // p指向它的next结点(q),并在2跳时检查tail结点是否更新,如果更新,则指向tail结点
}
}
初始队列

一开始,head和tail指针均指向“哑元”结点。
分支一,tail指向最后一个结点,直接插入
插入前

插入后

分支二,tail指向倒数第二个结点
插入前

向后推进,直至达到插入的条件

插入结点,并更新tail指针

分支三,tail滞后于head
插入前

情景1
tail指针未被其他线程更新

插入后

情景2
tail指针被其他线程更新

插入后

分支三的来历
A和B结点被删除,B结点next域指向自己,head指针更新至结点C

C结点被删除,head指针指向不变
接着,D结点被删除,head指针更新至结点E,此时,tail结点滞后于head结点,下图即为分支三初始情况

出队
public E poll() {
restartFromHead: for (;;) {
for (Node<E> h = head, p = h, q;;) { // h作为head的快照,p指向h结点
E item = p.item; // 获取p的item
if (item != null && p.casItem(item, null)) { // 如果item不为null, 表示此结点还未删除,尝试将其删除
if (p != h) // 两跳,更新一次head结点
updateHead(h, ((q = p.next) != null) ? q : p);
return item; // 返回此结点item
} else if ((q = p.next) == null) { // 否则,表示此结点已被删除,则查看其next结点是否存在
updateHead(h, p); // 若不存在,则更新head指针指向该结点
return null; // 并返回null
} else if (p == q) // 如果此结点已被删除,并且其next结点不为null,而是指向了自己,表示别的线程已经更新了head指针,为了找到有效结点,则从头开始(转向新的head结点)
continue restartFromHead;
else // 如果此结点已被删除,并且其next结点存在,向后推进,寻找未被删除结点
p = q;
}
}
}
分支一,head结点未被删除
删除前

删除后

分支二,head结点已被删除,且其next结点不存在

删除后,不变
或者

删除后

分支三,head结点已被删除,并且其next结点存在,并非指向自己
删除前

向后推进

删除后,且更新head结点

分支四,如果此结点已被删除,并且其next结点不为null,而是指向了自己,表示别的线程已经更新了head指针,如分支三
初始,同分支三删除前

向后推进p指针时,其他线程抢先删除结点,并更新了head指针,同分支三删除后

此时,应从头开始,重新获取head指针,进行后续操作。
行文至此结束。
尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_clq.html
【JUC源码解析】ConcurrentLinkedQueue的更多相关文章
- 【JUC源码解析】ScheduledThreadPoolExecutor
简介 它是一个线程池执行器(ThreadPoolExecutor),在给定的延迟(delay)后执行.在多线程或者对灵活性有要求的环境下,要优于java.util.Timer. 提交的任务在执行之前支 ...
- 【JUC源码解析】SynchronousQueue
简介 SynchronousQueue是一种特殊的阻塞队列,该队列没有容量. [存数据线程]到达队列后,若发现没有[取数据线程]在此等待,则[存数据线程]便入队等待,直到有[取数据线程]来取数据,并释 ...
- 【JUC源码解析】ForkJoinPool
简介 ForkJoin 框架,另一种风格的线程池(相比于ThreadPoolExecutor),采用分治算法,工作密取策略,极大地提高了并行性.对于那种大任务分割小任务的场景(分治)尤其有用. 框架图 ...
- 【JUC源码解析】DelayQueue
简介 基于优先级队列,以过期时间作为排序的基准,剩余时间最少的元素排在队首.只有过期的元素才能出队,在此之前,线程等待. 源码解析 属性 private final transient Reentra ...
- 【JUC源码解析】CyclicBarrier
简介 CyclicBarrier,一个同步器,允许多个线程相互等待,直到达到一个公共屏障点. 概述 CyclicBarrier支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后 ...
- 【JUC源码解析】Exchanger
简介 Exchanger,并发工具类,用于线程间的数据交换. 使用 两个线程,两个缓冲区,一个线程往一个缓冲区里面填数据,另一个线程从另一个缓冲区里面取数据.当填数据的线程将缓冲区填满时,或者取数据的 ...
- Jdk1.6 JUC源码解析(12)-ArrayBlockingQueue
功能简介: ArrayBlockingQueue是一种基于数组实现的有界的阻塞队列.队列中的元素遵循先入先出(FIFO)的规则.新元素插入到队列的尾部,从队列头部取出元素. 和普通队列有所不同,该队列 ...
- Jdk1.6 JUC源码解析(13)-LinkedBlockingQueue
功能简介: LinkedBlockingQueue是一种基于单向链表实现的有界的(可选的,不指定默认int最大值)阻塞队列.队列中的元素遵循先入先出 (FIFO)的规则.新元素插入到队列的尾部,从队列 ...
- Jdk1.6 JUC源码解析(6)-locks-AbstractQueuedSynchronizer
功能简介: AbstractQueuedSynchronizer(以下简称AQS)是Java并发包提供的一个同步基础机制,是并发包中实现Lock和其他同步机制(如:Semaphore.CountDow ...
随机推荐
- UVa 10735 - Euler Circuit(最大流 + 欧拉回路)
链接: https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem& ...
- 笔试面试之C++
7 类B是类A的公有派生类, 类A和类B中都定义了虚函数func(), p 是一个指向类A对象的指针,则p->A::func()将() A 调用类A中的函数 B 调用类B中的函数 C 根据p所指 ...
- Odoo工作流
转载请注明原文地址:https://www.cnblogs.com/cnodoo/p/9281264.html 一:工作流 工作流是与业务流程相关联的模型,可用于跟踪工序的动态演变过程. 工作流.活动 ...
- Android 面试题之编程
1.排序 package cn.java.suanfa; public class SuanFa { public static void main(String[] args) { int[] ar ...
- HDU 1829 A Bug's Life (种类并查集)
传送门: http://acm.hdu.edu.cn/showproblem.php?pid=1829 A Bug's Life Time Limit: 15000/5000 MS (Java/Oth ...
- Linux 带宽、CPU、内存占用情况
iftop 查看带宽占用情况(总)yum install -y iftop 安装iftopnethogs 查看进程流量 curl http://218.5.73.233:8060/ip.php 查看出 ...
- cocos2d-x开发: 完善接口范例分离模块
在上一篇文章中,我阐述了一下为什么要做分离这种工作,这篇文章没什么重点,只是将上一次没有完善的工作做完.原本我想做到像Lua-tests那样子,能够一次完全显示所有的tests,然后选择要执行的tes ...
- staticmethod classmethod
1. 静态方法 @staticmethod 只是名义上归类管,实际上静态方法里访问不了类或者实例中的任何属性 2. 类方法 @classmethod 只能访问类变量,不能访问实例变量 3.属性方法 @ ...
- iOS OC与JS的交互(JavaScriptCore实现)
本文包括JS调用OC方法并传值,OC调用JS方法并传值 本来想把html放进服务器里面,然后访问,但是觉得如果html在本地加载更有助于理解,特把html放进项目里 HTML代码 <!DOCTY ...
- Linux基础-1.Linux命令及获取帮助
1.Linux命令的格式 1)了解Linux命令的语法格式: 命令 [选项] [参数] 2)掌握命令格式中命令.选项.参数的具体的含义 a)命令:告诉Linux(UNIX)操作系统做(执行)什么 b) ...
