1.简介
ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部,当我们获取一个元素时,它会返回队列头部的元素。它采用了“wait-free”算法来实现,该算法在Michael & Scott算法上进行了一些修改。
2.offer()和add()方法
本质上offer和add是一样的。
public boolean offer(E e) {
/*这里判断传的值是不是空值,如果是空值就会报空指针*/
checkNotNull(e);
/*创建一个节点*/
final Node<E> newNode = new Node<E>(e);
/*由于是做无锁的cas操作,这里对添加元素做自旋的操作。
这里d两个变量t、p都指向尾节点*/
for (Node<E> t = tail, p = t;;) { <1>
Node<E> q = p.next;
if (q == null) {
/*判断尾节点的下一个节点是不是空值,
如果是空值,说明尾节点没有被修改,可以尝试做cas操作*/
if (p.casNext(null, newNode)) {
/*如果成功了,就说明添加元素成功,
下面的判断是有可能另一个线程执行到步骤<1>的t = tail
,尾节点发生了变化,要对尾节点进行cas操作。*/
if (p != t)
casTail(t, newNode);
return true;
}
}
else if (p == q) <2>
/*这里是说如果在插入的过程中,其他线程执行了poll操作,
导致了尾节点后移,有可能造成p==q,此时就要重新设置尾节点或者头结点。
1:此时将新的尾节点和之前的尾节点进行比较,如果不相同,就说明之前的
尾节点后移了,就要更新尾节点,然后再一次尝试插入操作。
2:如果现在的尾节点和之前的尾节点相同,就说明头结点已经和尾节点重合了
,就要重新赋值头结点。*/
p = (t != (t = tail)) ? t : head;
else <3>
/*如果都不是的话,就说明当前尾节点后面的元素不为空,
那就要将尾节点后移。
后移之前需要对尾节点做判断,排除尾节点和头结点重合,并且没有做
poll的操作改变尾节点。*/
p = (p != t && t != (t = tail)) ? t : q;
}
}