实现一个线程安全的队列有两种方式,一种是阻塞算法,另一种是非阻塞算法。使用阻塞算法有两种方式:两把锁(出队和入队采用不同的锁),或者出队入队采用同一把锁。而非阻塞算法可以采用循环CAS的方式来实现。ConcurrentLinkedQueue采用基于链接节点的无界安全队列来实现,采用wait-free算法(即CAS)来实现。
offer()方法
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为最后节点,直接赋值
if (p.casNext(null, newNode)) {//0
//如果p!=t说明tail节点不是最后节点,直接赋值为最新节点
if (p != t)
casTail(t, newNode);//1
return true;
}
}
else if (p == q)
//p==q时,将p赋值head节点或者t()
p = (t != (t = tail)) ? t : head;//2
else
//将p赋值为最后节点在进行加入
p = (p != t && t != (t = tail)) ? t : q;//3
}
}
一个空队列依次加入1、2、3、4的过程如下:
- 加入第1个元素1时,此时tail和head节点都为null,next也为null。由于p==p.next,直接执行第0步进行赋值。赋值完成后head节点为1,next为null;tail节点仍为null,next节点指向自身。
- 加入第2个元素2时,由于tail节点的next指向自身所以p==q且p!=null,此时先执行第2步将p=head,此时排为最后节点,然后执行第0步和第1步,将newNode加到队尾,将tail节点指向队尾。
- 加入第3个元素3时tail.next==null,直接执行第0步赋值。
- 加入第四个元素4时,tail.next!=null&&tail!=tail.next,此时执行第3步p=p.next,然后分别执行第0、1步。
注意tail节点并不总是指向最后一个节点,head节点在第一次offer()之后指向第一个节点。