ConcurrentLinkedQueue

主要讲一下在JDK8中,ConcurrentLikedQueue是如何入队,出队的。

首先我们要明白,ConcurrentLikedQueue是一种安全的没有边界的基于链表的队列,有头节点head,尾结点tail。

类似于           ,上图是创建一个空的队列,只有head和tail节点,以下为源码:

public ConcurrentLinkedQueue() {
        head = tail = new Node<E>(null);
}

对于入队来说,我们要了解的是tail节点不一定是最后一个节点,这是非常重要的。一般来说,当tail节点的next不为空时,在队尾加入新节点,更新tail指向尾节点;当tail的next节点为空时,在队尾加入新节点,不更新tail的位置。

源码中有一个注释:Both head and tail are permitted to lag.  In fact, failing to update them every time one could is a significant optimization (fewer CASes). As with LinkedTransferQueue (see the internal documentation for that class), we use a slack threshold of two; that is, we update head/tail when the current pointer appears to be two or more steps away from the first/last node.
 大概的意思是说不用每次都更新头尾节点,这是一个非常重要的优化。 使用的松弛阈值为2; 也就是说,当当前指针距离第一个/最后一个节点有两个或更多节点的距离时,我们更新head/tail。

在入队的时候,我们就能够很容易的看到上述所描述tail的特点。

入队:

 /**
* Inserts the specified element at the tail of this queue.
* As the queue is unbounded, this method will never return {@code false}.
* 将指定的元素插入到此队列的末尾。因为队列是无界的,所以这个方法永远不会返回{@code false}。 * @return {@code true} (as specified by {@link Queue#offer})
* @throws NullPointerException if the specified element is null
*/
public boolean offer(E e) {
checkNotNull(e);
final Node<E> newNode = new Node<E>(e); //入队前构建节点 //从尾结点开始入队
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) { //tail是尾结点
// p is last node
//如果p是尾结点,设置p节点的next为newNode
if (p.casNext(null, newNode)) { //如果新节点添加入尾节点后面
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
//成功的CAS是使e成为这个队列的一个元素,使newNode成为“活的”的线性化点。
if (p != t) // hop two nodes at a time一次跳转两个节点
casTail(t, newNode); // Failure is OK.
return true;
}
// Lost CAS race to another thread; re-read next
//丢失的CAS争用到另一个线程;重读next
}
else if (p == q)
// We have fallen off list. If tail is unchanged, it
// will also be off-list, in which case we need to
// jump to head, from which all live nodes are always
// reachable. Else the new tail is a better bet. p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
p = (p != t && t != (t = tail)) ? t : q;
}
}

上述的offer(E e)方法,即是向队列尾部添加元素。

首先,入队前构建结点newNode,接下来无限for循环,保证入队成功,所以该方法返回总是true。

假设现在队列中有两个结点a,b,tail指向b。t->tail,p->t,如下图:

假设现在要添加一个c结点,那么会出现下图情况:

如果再添加一个D结点,

(1)因为p结点的后继结点q不为null,并且q也不等于p,所以经过判断后,p指向q,tail不变。如下图:

(2)q继续指向p的next结点,因为q为null,所以把d结点设置为p结点的next结点,因为p不指向t,所以把t指向为尾结点。如下图:

总结下来,我们发现,首先总会获取到尾结点,然后用CAS算法进行结点入队。

ConcurrentLinkedQueue (一)的更多相关文章

  1. 队列送券的实际应用--ConcurrentLinkedQueue并发队列

    1.TicketQueue.java--队列封装类,负责如下职责:a.把活动登记对象放入队列中b.从队列中获取活动登记对象,并派券 package com.datong.pear.ticket; im ...

  2. 【JUC】JDK1.8源码分析之ConcurrentLinkedQueue(五)

    一.前言 接着前面的分析,接下来分析ConcurrentLinkedQueue,ConcurerntLinkedQueue一个基于链接节点的无界线程安全队列.此队列按照 FIFO(先进先出)原则对元素 ...

  3. Java 线程 — ConcurrentLinkedQueue

    ConcurrentLinkedQueue 在考虑并发的时候可以先考虑单线程的情况,然后再将并发的情况考虑进来. 比如ConcurrentLinkedQueue: 先考虑单线的offer 再考虑多线程 ...

  4. Java多线程系列--“JUC集合”10之 ConcurrentLinkedQueue

    概要 本章对Java.util.concurrent包中的ConcurrentHashMap类进行详细的介绍.内容包括:ConcurrentLinkedQueue介绍ConcurrentLinkedQ ...

  5. [Java 基础] 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法

    reference : http://www.cnblogs.com/linjiqin/archive/2013/05/30/3108188.html 在Java多线程应用中,队列的使用率很高,多数生 ...

  6. 并发队列ConcurrentLinkedQueue和阻塞队列LinkedBlockingQueue用法

    在Java多线程应用中,队列的使用率很高,多数生产消费模型的首选数据结构就是队列(先进先出).Java提供的线程安全的Queue可以分为阻塞队列和非阻塞队列,其中阻塞队列的典型例子是BlockingQ ...

  7. 阻塞队列LinkedBlockingQueue和并发队列ConcurrentLinkedQueue

    LinkedBlockingQueue: public class LinkedBlockingQueue<E> extends AbstractQueue<E> implem ...

  8. CAS无锁算法与ConcurrentLinkedQueue

    CAS:Compare and Swap 比较并交换 java.util.concurrent包完全建立在CAS之上的,没有CAS就没有并发包.并发包借助了CAS无锁算法实现了区别于synchroni ...

  9. java线程控制、状态同步、volatile、Thread.interupt以及ConcurrentLinkedQueue

    在有些严格的系统中,我们需要做到干净的停止线程并清理相关状态.涉及到这个主题会带出很多的相关点,简单的总结如下: 我们知道,在java中,有一个volatile关键字,其官方说明(https://do ...

  10. java LinkedBlockingQueue和ConcurrentLinkedQueue的区别

    实现上看,两者都继承于AbstractQueue,但是ConcurrentLinkedQueue实现了Queue,而LinkedBlockingQueue实现了BlockingQueue,Blocki ...

随机推荐

  1. 一篇文章学会shell脚本

    一.Shell传递参数 #!/bin/bash # 假设在脚本运行时写了三个参数 ..,,则 "(传递了三个参数). echo "-- \$* 演示 --" for i ...

  2. Linux开机报错,提示根目录有错误,无法通过检测进入系统

    报错信息如下: VolGroup-lv_root contains a file system with errors, check forced. 修复方法:(因为我的是虚拟机,可以随意做备份,所以 ...

  3. 【Tools】UltraISO官网最新板+注册码

    官网最新UltraISO 9.7版本安装文件,非注册机,亲测可用,若注册码失效,评论会删除. 土豪赏逼地址: https://download.csdn.net/download/qq_1818716 ...

  4. 源码分析之Handler

    Handler是Android中的消息机制实现,可以实现UI线程和子线程的消息传递,这里就来深入了解Android的消息机制,来分析Handler的源代码 入手实例 在Android开发中,子线程与主 ...

  5. Ansible-Hoc--样例

    一.常用场景 1. 列出支持的模块及模块功能说明: 2. sudo用法: 3. 检查服务器存活,复制本地文件到远程: 4. 多线程判断服务器的存活: 5.  显示所有主机的hostname: 6. 列 ...

  6. python实践项目七:正则表达式版本的strip()函数

    描述:写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样.如果只传入了要去除的字符串, 没有其他参数, 那么就从该字符串首尾去除空白字符:否则, 函数第二个参数指定的字符将从该字符 ...

  7. 系统中提示未找到/usr/bin/ld: cannot find -lxxx错误的通用解决方法

    在linux环境编译应用程式或lib的source code时常常会出现如下的错误讯息: 代码如下: /usr/bin/ld: cannot find -lxxx 这些讯息会随着编译不同类型的sour ...

  8. 嵌入式qt显示中文和隐藏鼠标

    最近项目快接近尾声了,要把项目移植到板子上,但是板子上的系统没有安装字库,导致中文无法显示,并且有一个很讨厌的鼠标光标(又没有鼠标),上网找了一些解决方案,记录一下 qt显示中文: 如果你急于在ARM ...

  9. c++连接打印机(转载)

    Visual C++6.0是开发Windows应用程序的强大工具,但是要通过它实现程序的打印功能,一直是初学者的一个难点,经常有朋友询问如何在VC中实现打印功能,他们往往感到在MFC提供的框架内实现这 ...

  10. 转载:MFC之MessageBox、AfxMessageBox用法

    在软件中我们经常会弹出个小窗口,给一点点提示.这就会用到消息对话框. 在Win32 API程序中只有MessageBox这一种用法. 而在MFC中就有三各方法: .调用API中的MessageBox: ...