ConcurrentLinkedQueue

Posted xp1234

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentLinkedQueue相关的知识,希望对你有一定的参考价值。

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的特点。

入队:

 1 /**
 2      * Inserts the specified element at the tail of this queue.
 3      * As the queue is unbounded, this method will never return {@code false}.
 4      * 将指定的元素插入到此队列的末尾。因为队列是无界的,所以这个方法永远不会返回{@code false}。
 5              
 6      * @return {@code true} (as specified by {@link Queue#offer})
 7      * @throws NullPointerException if the specified element is null
 8      */
 9     public boolean offer(E e) {
10         checkNotNull(e);
11         final Node<E> newNode = new Node<E>(e);  //入队前构建节点
12 
13         //从尾结点开始入队
14         for (Node<E> t = tail, p = t;;) {
15             Node<E> q = p.next;  
16             if (q == null) {   //tail是尾结点
17                 // p is last node
18                 //如果p是尾结点,设置p节点的next为newNode
19                 if (p.casNext(null, newNode)) {  //如果新节点添加入尾节点后面
20                     // Successful CAS is the linearization point
21                     // for e to become an element of this queue,
22                     // and for newNode to become "live".
23                     //成功的CAS是使e成为这个队列的一个元素,使newNode成为“活的”的线性化点。
24                     if (p != t) // hop two nodes at a time一次跳转两个节点
25                         casTail(t, newNode);  // Failure is OK.
26                     return true;
27                 }
28                 // Lost CAS race to another thread; re-read next
29                 //丢失的CAS争用到另一个线程;重读next
30             }
31             else if (p == q)
32                 // We have fallen off list.  If tail is unchanged, it
33                 // will also be off-list, in which case we need to
34                 // jump to head, from which all live nodes are always
35                 // reachable.  Else the new tail is a better bet.
36                 //我们从list上掉下来了。
37                 //如果tail不变,它也是off-list,
38                 //在这种情况下,我们需要跳转到head,从head可以到达所有活动节点。
39                 //否则,新tail是个更好的选择。
40                 
41                 p = (t != (t = tail)) ? t : head;
42             else
43                 // Check for tail updates after two hops.
44                 p = (p != t && t != (t = tail)) ? t : q;
45         }
46     }

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

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

技术图片

 

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

 

技术图片

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

以上是关于ConcurrentLinkedQueue的主要内容,如果未能解决你的问题,请参考以下文章

初始化后如何更新java.util.concurrent.ConcurrentLinkedQueue#head?

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

我通过调试ConcurrentLinkedQueue发现一个IDEA的小虫子(bug), vscode复现, eclipse毫无问题

《Java并发编程的艺术》之ConcurrentLinkedQueue

Java并发多线程编程——并发容器ConcurrentLinkedQueue

死磕 java集合之ConcurrentLinkedQueue源码分析