由于LinkedBlockingDeque作为双端队列的实现,采用了单锁的保守策略使其不利于多线程并发情况下的使用,故ConcurrentLinkedDeque应运而生,它是一种基于链表的无界的同时支持FIFO、LIFO的非阻塞并发双端队列,当许多线程共享对公共集合的访问时,ConcurrentLinkedDeque是一个合适的选择,类比ConcurrentLinkedQueue是针对LinkedBlockingQueue对高并发情况的一种解决方案,ConcurrentLinkedDeque也是同样的地位,都是采用 CAS来替代加锁,甚至ConcurrentLinkedDeque再实现上也与ConcurrentLinkedQueue有很多相似的地方,其中最值得提及的就是,它采用了与ConcurrentLinkedQueue一样的松弛阀值设计(松弛阀值都是1),即head、tail并不总是指向队列的第一个、最后一个节点,而是保持head/tail距离第一个/最后一个节点的距离不超过1个节点的距离,从而减少了更新head/tail指针的CAS次数。Java Doc指出理解ConcurrentLinkedQueue的实现是理解该类实现的先决条件,所以最好先理解了ConcurrentLinkedQueue再来理解该类。
ConcurrentLinkedDeque另外还使用了两种方法来减少volatile写的次数:一是使用单次CAS操作来一次性使多次连续的CAS生效;二是将对同一块内存地址的volatile写与普通写混合。它的节点类与LinkedBlockingDeque的属性一致都是数据item、prev、next,只是多了一些CAS操作方法。与ConcurrentLinkedQueue一样,只有那些数据item不为空的节点才被认为是活动的节点,当将item置为null时,意味着从队列中逻辑删除掉了。
与LinkedBlockingDeque一样,任何时候,队列的第一个节点"first"的前驱prev为null,队列的最后一个节点"tail"的next后继为null。“first”和“last”节点可能是活动的,也可能不是活动的。“first”和“last”节点总是相互可达的。通过将第一个或最后一个节点的空前驱或后继CAS引用到包含指定元素的新节点,实现原子性地添加一个新元素,从而元素的节点在那时原子性地变成“活动的”,如果一个节点是活动的(item不为null)或者它是first/last节点,我们都称为是有效节点。ConcurrentLinkedDeque同样采用了“自链接(p.prev = p或p.next = p)”的方式使节点断开与队列的链接,有效活动节点不会有自链接的情况。
前面说了ConcurrentLinkedDeque有两个不总是指向第一个/最后一个节点的head、tail指针,所以它并没有像LinkedBlockingDeque那样设计first、tail属性,但是first、tail总是可以通过head、tail在O(1)时间内找到。
ConcurrentLinkedDeque删除节点分三个阶段:
- logical deletion(逻辑删除):通过CAS将数据item置为null,使该节点满足解除链接(unlinking)的条件。
- unlinking(解除链接):该阶段使队列中的活动节点无法到达该节点,但是保留该节点到队列中活动节点的链接,从而最终可由GC回收。此阶段典型的就是被迭代器使用的时候,使迭代器可以继续往下迭代。
- gc-unlinking:该阶段进一步解除被删除节点到队列中活动节点的链接,使其更容易被GC回收,通过让节点自链接或链接到终止节点(PREV_TERMINATOR 或 NEXT_TERMINATOR)来实现。这一步是为了使数据结构保持GC健壮性(gc-robust),消除使用保守式GC(conservative GC,目前已经很少使用)对内存无限期滞留的风险,并提高了分代GC的性能。
由于删除节点的第二、三阶段都不是保证数据正确性必须的,仅仅是对迭代器与内存的优化,故适当的减少这些操作的次数对性能是一种提高。所以ConcurrentLinkedDeque不仅设计了同ConcurrentLinkedQueue一样针对head、tail节点的松弛阈值,而且还提供了针对解除删除节点链接的阈值HOPS,也就是只有当逻辑删除的节点个数达到一定数量才会触发unlinking 和gc-unlinking,这样也是对性能的一种优化。
同ConcurrentLinkedQueue一样,ConcurrentLinkedDeque也对head、tail设定了如下的一些不变与可变性约束:
head/tail的不变性:
- 第一个节点总是可从head通过prev链接在O(1)时间复杂度内访问到。
- 最后一个节点总是可以从tail通过next链接在O(1)时间复杂度内访问到。
- 所有活动节点(item不为null)都可以从第一个节点通过succ()访问。
- 所有活动节点(item不为null)都可以从最后一个节点通过pred()访问。
- head和tail都不会为null。
- head节点的next不会指向自身形成自连接。
- head/tail不会是GC-unlinked节点(但它可能是unlink节点)。
head/tail的可变性:
- head、tail的数据item可以为null,也可以不为null。
- head可能无法从第一个或最后一个节点或从tail到达。
- tail可能无法从第一个或最后一个节点或从head到达。
下面开始分析ConcurrentLinkedDeque的源码,ConcurrentLinkedDeque和ConcurrentLinkedQueue并没有继承相应的BlockingQueue/BlockingQueue,容量又是无界的,所以不存在阻塞方法。
源码解析
首先看它的一些基础字段、节点内部类以及构造方法:
1 /** 2 * A node from which the first node on list (that is, the unique node p with p.prev == null && p.next != p) can be reached in O(1) time. 3 可以在O(1)时间内从列表中的第一个节点到达的节点(即,具有p.prev == null && p.next!= p的唯一节点p)。 4 5 * Invariants: 不变性 6 * - the first node is always O(1) reachable from head via prev links 第一个节点总是可从head通过prev链接在O(1)时间内访问到 7 * - all live nodes are reachable from the first node via succ() 所有活动节点都可以从第一个节点通过succ()访问 8 * - head != null head不为空 9 * - (tmp = head).next != tmp || tmp != head 10 * - head is never gc-unlinked (but may be unlinked) 。head永远不会gc-unlinked(但可能是unlinked) 11 12 * Non-invariants: 可变性 13 * - head.item may or may not be null head 的数据项可以为空 14 * - head may not be reachable from the first or last node, or from tail 。 head 可能无法从第一个或最后一个节点或从tail到达。 15 */ 16 private transient volatile Node<E> head; 17 18 /** 19 * A node from which the last node on list (that is, the unique node p 20 * with p.next == null && p.prev != p) can be reached in O(1) time. 21 可以在O(1)时间内从列表中的最后一个节点到达的节点(即具有p.next == null && p.prev!= p的唯一节点p)。 22 23 * Invariants: 不变性 24 * - the last node is always O(1) reachable from tail via next links。最后一个节点始终可以通过下一个链接从tail访问在O(1)时间内访问到 25 * - all live nodes are reachable from the last node via pred() 。所有活动节点都可以从最后一个节点通过pred()访问 26 * - tail != null tail不为空 27 * - tail is never gc-unlinked (but may be unlinked) tail永远不会gc-unlinked(但可能是unlinked) 28 29 * Non-invariants: 可变性 30 * - tail.item may or may not be null tail的数据项可以为空 31 * - tail may not be reachable from the first or last node, or from head tail可能无法从第一个或最后一个节点或从head访问到。 32 */ 33 private transient volatile Node<E> tail; 34 35 /**指示出队节点的终结节点*/ 36 private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR; 37 38 39 @SuppressWarnings("unchecked") 40 Node<E> prevTerminator() { //从对头出队节点的前向终结节点 41 return (Node<E>) PREV_TERMINATOR; 42 } 43 44 @SuppressWarnings("unchecked") 45 Node<E> nextTerminator() { //从对尾出队节点的后继终结节点 46 return (Node<E>) NEXT_TERMINATOR; 47 } 48 49 static final class Node<E> { 50 volatile Node<E> prev; 51 volatile E item; 52 volatile Node<E> next; 53 54 Node() { // default constructor for NEXT_TERMINATOR, PREV_TERMINATOR 55 } 56 57 /** 58 * Constructs a new node. Uses relaxed write because item can 59 * only be seen after publication via casNext or casPrev. 60 */ 61 Node(E item) { 62 UNSAFE.putObject(this, itemOffset, item); 63 } 64 65 boolean casItem(E cmp, E val) { 66 return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); 67 } 68 69 void lazySetNext(Node<E> val) { 70 UNSAFE.putOrderedObject(this, nextOffset, val); 71 } 72 73 boolean casNext(Node<E> cmp, Node<E> val) { 74 return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val); 75 } 76 77 void lazySetPrev(Node<E> val) { 78 UNSAFE.putOrderedObject(this, prevOffset, val); 79 } 80 81 boolean casPrev(Node<E> cmp, Node<E> val) { 82 return UNSAFE.compareAndSwapObject(this, prevOffset, cmp, val); 83 } 84 85 // Unsafe mechanics 86 private static final sun.misc.Unsafe UNSAFE; 87 private static final long prevOffset; 88 private static final long itemOffset; 89 private static final long nextOffset; 90 91 static { 92 try { 93 UNSAFE = sun.misc.Unsafe.getUnsafe(); 94 Class<?> k = Node.class; 95 prevOffset = UNSAFE.objectFieldOffset 96 (k.getDeclaredField("prev")); 97 itemOffset = UNSAFE.objectFieldOffset 98 (k.getDeclaredField("item")); 99 nextOffset = UNSAFE.objectFieldOffset 100 (k.getDeclaredField("next")); 101 } catch (Exception e) { 102 throw new Error(e); 103 } 104 } 105 } 106 107 //针对被删除节点进行unlinking/GC-unlinking的阈值 108 private static final int HOPS = 2; 109 110 private boolean casHead(Node<E> cmp, Node<E> val) { 111 return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); 112 } 113 114 private boolean casTail(Node<E> cmp, Node<E> val) { 115 return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); 116 } 117 118 // Unsafe mechanics 119 private static final sun.misc.Unsafe UNSAFE; 120 private static final long headOffset; 121 private static final long tailOffset; 122 static { 123 PREV_TERMINATOR = new Node<Object>(); 124 PREV_TERMINATOR.next = PREV_TERMINATOR; 125 NEXT_TERMINATOR = new Node<Object>(); 126 NEXT_TERMINATOR.prev = NEXT_TERMINATOR; 127 try { 128 UNSAFE = sun.misc.Unsafe.getUnsafe(); 129 Class<?> k = ConcurrentLinkedDeque.class; 130 headOffset = UNSAFE.objectFieldOffset 131 (k.getDeclaredField("head")); 132 tailOffset = UNSAFE.objectFieldOffset 133 (k.getDeclaredField("tail")); 134 } catch (Exception e) { 135 throw new Error(e); 136 } 137 } 138 139 /** 140 * Constructs an empty deque. 默认构造方法,head、tail都指向同一个item为null的节点 141 */ 142 public ConcurrentLinkedDeque() { 143 head = tail = new Node<E>(null); 144 } 145 146 /** 147 * Constructs a deque initially containing the elements of 148 * the given collection, added in traversal order of the 149 * collection's iterator. 150 * 151 * @param c the collection of elements to initially contain 152 * @throws NullPointerException if the specified collection or any 153 * of its elements are null 154 */ 155 public ConcurrentLinkedDeque(Collection<? extends E> c) { 156 // Copy c into a private chain of Nodes 157 Node<E> h = null, t = null; 158 for (E e : c) { 159 checkNotNull(e); 160 Node<E> newNode = new Node<E>(e); 161 if (h == null) 162 h = t = newNode; 163 else { 164 t.lazySetNext(newNode); 165 newNode.lazySetPrev(t); 166 t = newNode; 167 } 168 } 169 initHeadTail(h, t); 170 } 171 172 /** 173 * Initializes head and tail, ensuring invariants hold. 174 * 初始化head和tail,确保它们的不变性 175 */ 176 private void initHeadTail(Node<E> h, Node<E> t) { 177 if (h == t) { //队列为空,或者只有一个元素 178 if (h == null) 179 h = t = new Node<E>(null);//队列为空,head、tail都指向同一个item为null的节点 180 else { 181 // 只有一个元素,重新构造一个节点指向tail,避免head、tail都指向同一个非null节点 182 // Avoid edge case of a single Node with non-null item. 183 Node<E> newNode = new Node<E>(null); 184 t.lazySetNext(newNode); 185 newNode.lazySetPrev(t); 186 t = newNode; 187 } 188 } 189 head = h; 190 tail = t; 191 }