ConcurrentLinkedQueue使用链表作为其数据结构,节点Node定义如下:
private static class Node<E> {
// 目标元素
volatile E item;
// 当前节点的下一个元素
volatile Node<E> next;
/**
* Constructs a new node. Uses relaxed write because item can
* only be seen after publication via casNext.
*/
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
}
// 设置当前Node的item值,cmp为期望值,val为设置目标值;
// 当当前值等于期望值时,将目标设置为val;
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
// 设置next字段值,cmp为期望值,val为设置目标值;
// 当当前值等于期望值时,将目标设置为val;
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
...
}
ConcurrentLinkedQueue类内部两个重要字段,head和tail,分别表示头部和尾部;
private transient volatile Node<E> head;
private transient volatile Node<E> tail;
无参构造方法初始化head和tail:
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
向队列添加元素,方法没有锁操作,线程安全由CAS操作和队列的算法来保证;
/**
* Inserts the specified element at the tail of this queue.
* As the queue is unbounded, this method will never return {@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) {
// p is last 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.
return true;
}
// Lost CAS race to another thread; re-read 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;
}
}
核心是for循环,只有添加成功后返回true才能退出循环;
当第一次加入元素时,队列为空,所以p.next为null,进入第一层判断,表明p是最后一个节点,CAS尝试将p的next节点赋值为newNode,即将第一个元素加入队列;如果casNext()失败,循环尝试,直至成功;如果成功,此时p==t,直接返回true,退出方法;所以新增第一个元素时,tail不会被更新;
当增加第二个元素时,tail没有被更新,p=t=tail=head,tail还指向head节点位置,所以p.next实际指向的是第一个元素,此时q!=null&p!=q,所以跳转到最后else语句中,因为p==t,给p赋新值,即p=q,p实际指向了第一个元素,此时进入下一层循环时,p.next为null,进入第一层判断,CAS尝试将p的next节点赋新值newNode,如果casNext()失败,循环尝试直到成功;如果成功,此时p指向第一个元素,而t指向head节点,所以p!=t,尝试更新tail,即将tail由t值所在位置(此时为head节点)更新为新加入元素(第二个元素);
当p==q时,此节点为哨兵节点,next指向自己,这个节点已经不在队列中存在了,表示要删除的节点或空节点;此时无法通过next获得后续节点,所以要返回head节点(所有的节点都可以从head节点获得);一旦在执行过程中发生tail节点被其他线程修改的情况,则使用新的tail作为链表末尾,避免了从head节点重新查找tail的开销;
“!=”不是原子操作,可以被中断,在执行“!=”操作时,会先取得t值,再执行t=tail,取得t的新值,然后再进行比较两个值是否相等;
p = (t != (t = tail)) ? t : head;
哨兵节点产生情况:
ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
queue.add("1");
queue.poll();
queue.add("2");
poll()方法源码如下:
public E poll() {
restartFromHead:
for (;;) {
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
// Successful CAS is the linearization point
// for item to be removed from this queue.
if (p != h) // hop two nodes at a time
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
}
}
队列中只有1个元素,此时tail并没有更新,指向和head相同的位置;此时head本身的item为null,next指向第一个元素;
在第一次循环时,代码直接进入最后一个else语句,p=q,此时p、q指向第一个元素;
第二轮循环中,p.item为“1”,不为null,如果CAS操作成功,进入第一个if语句中,p.casItem()将p的item设置为null(删除操作,将第一个元素置空);此时h指向head,而p指向刚刚置空的第一个元素,所以不相等;p.next为null,赋值null给q,((q = p.next) != null) ? q : p)返回p,进入updateHead(h,p)方法;
/**
* Tries to CAS head to p. If successful, repoint old head to itself
* as sentinel for succ(), below.
*/
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
可以看到,通过casHead(h,p)将p作为新的链表头部,而原有的head就被h.lazySetNext(h)设置为哨兵节点;
此时原有的head头部和tail实际上是同一个元素,因此,再次用offer()方法插入元素时,就会遇到这个哨兵节点;