ConcurrentLinkedQueue的几个细节问题

ABA问题

源码注释中对这个问题的说明

Note that like most non-blocking algorithms in this package,this implementation relies on the fact that in garbage collected systems, there is no possibility of ABA problems due to recycled nodes, so there is no need to use “counted pointers” or related techniques seen in versions used in non-GC’ed settings.

When constructing a Node (before enqueuing it) we avoid paying for a volatile write to item by using Unsafe.putObject instead of a normal write. This allows the cost of enqueue to be “one-and-a-half” CASes.（避免写volatile操作的开销，使得入队操作的cost相当与1.5个CAS操作）

引申

putXXX(long address, XXX value): Will place the specified value of type XXX directly at the specified address.(指定地址保存变量)

putXXXVolatile(Object target, long offset, XXX value)Will place value at target’s address at the specified offset and not hit any thread local caches(不会被加载到线程本地缓存，只保存在主存，保证可见性)

putOrderedXXX(Object target, long offset, XXX value): Will place value at target’s address at the specified offet and might not hit all thread local caches.(不保证可见性)

offer()方法

jdk1.7中更改了offer的写法，变得更简洁

123456789101112131415161718192021222324252627282930

publicbooleanoffer(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) {// p is last node//Node的入队操作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".if (p != t) // hop two nodes at a time casTail(t, newNode); // Failure is OK.returntrue; }// Lost CAS race to another thread; re-read next }elseif (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; } }

对于第24行，(t!=(t=tail))的执行顺序是这样的

t_old=t

t=tail

t_old!=t在执行比较之前tail被赋值给t(new)

弱一致迭代器导致的GC问题

在LinkedBlockingQueue也有类似描述

That would cause two problems:

- allow a rogue Iterator to cause unbounded memory retention
- cause cross-generational linking of old Nodes to new Nodes if a Node was tenured while live, which generational GCs have a

hard time dealing with, causing repeated major collections.However, only non-deleted Nodes need to be reachable from dequeued Nodes, and reachability does not necessarily have to be of the kind understood by the GC. We use the trick of linking a Node that has just been dequeued to itself. Such a self-link implicitly means to advance to head.

常见的使用错误

谨慎使用addAll()方法

批量操作不保证原子性，执行addlAll方法是并发的迭代操作可能看不到全部添加到队列的元素。Additionally, the bulk operations addAll, removeAll, retainAll, containsAll, equals, and toArray are not guaranteed to be performed atomically. For example, an iterator operating concurrently with an addAll operation might view only some of the added elements.

谨慎使用size()方法

head和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. Since head and tail are updated concurrently and independently, it is possible for tail to lag behind head (why not)?