1. 介绍
无界的线程安全的队列,FIFO,无锁+链表算法实现,不阻塞
2. 源码
2.1 Node<E> 静态内部类
item: 当前节点元素
next: 下一个节点
设置元素使用无锁算法实现
2.2 构造函数
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
public ConcurrentLinkedQueue(Collection<? extends E> c) {
Node<E> h = null, t = null;
for (E e : c) {
checkNotNull(e);
Node<E> newNode = new Node<E>(e);
if (h == null)
h = t = newNode;
else {
t.lazySetNext(newNode);
t = newNode;
}
}
if (h == null)
h = t = new Node<E>(null);
head = h;
tail = t;
}
2.2 offer
public boolean offer(E e) {
//检查节点是否为null
checkNotNull(e);
// 创建新节点
final Node<E> newNode = new Node<E>(e);
//死循环 直到成功为止
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
// q == null 表示 p已经是最后一个节点了,尝试加入到队列尾
// 如果插入失败,则表示其他线程已经修改了p的指向
if (q == null) { // --- 1
// casNext:t节点的next指向当前节点
// casTail:设置tail 尾节点
if (p.casNext(null, newNode)) { // --- 2
// node 加入节点后会导致tail距离最后一个节点相差大于一个,需要更新tail
if (p != t) // --- 3
casTail(t, newNode); // --- 4
return true;
}
}
// p == q 等于自身
else if (p == q) // --- 5
// p == q 代表着该节点已经被删除了
// 由于多线程的原因,我们offer()的时候也会poll(),如果offer()的时候正好该节点已经poll()了
// 那么在poll()方法中的updateHead()方法会将head指向当前的q,而把p.next指向自己,即:p.next == p
// 这样就会导致tail节点滞后head(tail位于head的前面),则需要重新设置p
p = (t != (t = tail)) ? t : head; // --- 6
// tail并没有指向尾节点
else
// tail已经不是最后一个节点,将p指向最后一个节点
p = (p != t && t != (t = tail)) ? t : q; // --- 7
}
}
初始化
添加第一个元素A
p = t = tail, q = p.next = null,第一个if判断成功,p.next = A
添加第二个元素B
p = t = tail, q = p.next != null, 走最后一个else,p = q,
q = p.next, q == null,走第一个if
后面插入依次是这个过程
2.3 poll
public E poll() {
// 如果出现p被删除的情况需要从head重新开始
restartFromHead: // 这是什么语法?真心没有见过
for (;;) {
for (Node<E> h = head, p = h, q;;) {
// 节点 item
E item = p.item;
// item 不为null,则将item 设置为null
if (item != null && p.casItem(item, null)) { // --- 1
// p != head 则更新head
if (p != h) // --- 2
// p.next != null,则将head更新为p.next ,否则更新为p
updateHead(h, ((q = p.next) != null) ? q : p); // --- 3
return item;
}
// p.next == null 队列为空
else if ((q = p.next) == null) { // --- 4
updateHead(h, p);
return null;
}
// 当一个线程在poll的时候,另一个线程已经把当前的p从队列中删除——将p.next = p,p已经被移除不能继续,需要重新开始
else if (p == q) // --- 5
continue restartFromHead;
else
p = q; // --- 6
}
}
}
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
在updateHead中h指向了自己,所以在offer中,会有p==q的情况,代表节点已经被删除了
此时,head在tail前面,只能重新指向head,然后找新tail
p = (t != (t = tail)) ? t : head;