ConcurrentLinkedQueue
一、 类图
二、 构造方法
public class ConcurrentLinkedQueue<E> .... {
// 头节点,
private transient volatile Node<E> head;
// 尾节点,尾节点不一定是链表的最后一个节点
private transient volatile Node<E> tail;
// 构造
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
......................
}
- 头节点 head 和尾节点 tail 都被 volatile 修饰
- 执行构造方法节点变化如下所示:
三、 offer方法
-
源码
public boolean offer(E e) { // 判空,为空则抛出空指针异常 checkNotNull(e); // 创建要添加的节点 final Node<E> newNode = new Node<E>(e); // 无限循环,入队不成功,则反复入队 // t 表示 tail 节点 // p 表示链表的尾节点,默认等于 tail 节点 for (Node<E> t = tail, p = t;;) { // q 为尾节点的下一个节点 Node<E> q = p.next; // 如果尾节点的下一个节点为空,则表示 p 为尾节点 if (q == null) { // CAS 设置尾节点的下一个节点为新添加的节点,如果设置失败,在再次尝试 if (p.casNext(null, newNode)) { // 如果tail节点有大于等于1个的 next 节点,则更新 tail 节点,将新添加的节点设置为 tail 节点 if (p != t) // 相当于循环两次更新一次 tail 节点 casTail(t, newNode); // 新添加的节点设置为tail节点,允许失败,失败了表示有其他线程成功更新了tail节点 return true; } } else if (p == q) // 只有在尾节点和尾节点的下一个节点为空的情况下成立 p = (t != (t = tail)) ? t : head; else // 把 tail节点设置为为尾节点,再次循环设置下一个节点 p = (p != t && t != (t = tail)) ? t : q; } }
-
offer方法做了什么
- 新增元素都放到单链的尾部,就做了这么一件事情
3.1、 添加第一个节点
for (Node<E> t = tail, p = t;;) {
Node<E> q = p.next;
if (q == null) {
if (p.casNext(null, newNode)) {
if (p != t)
casTail(t, newNode);
return true;
}
}
这个时候下一个节点为null,也就是q==null, 执行代码将新添加的节点设置为尾节点的下一个节点。
这个时候p节点和 t节点相等,都指向尾节点,不会更新 tail 节点,设置成功后,链表如下:
3.2、 添加第二个节点
if (q == null) {
…
}
else if (p == q)
// p = q = null
p = (t != (t = tail)) ? t : head;
else
p = (p != t && t != (t = tail)) ? t : q;
创建一个节点,执行for循环,这个是q节点是有内容的(node),所以不会执行q==null的逻辑
又因为p!=q,所以执行 p = (p != t && t != (t = tail)) ? t : q。
这句代码是什么意思呢,很多人看不懂,其实很简单
-
就是将p指向尾节点
- t != (t = tail):如果t节点和尾节点(tail)不一样了,因为tail是线程可见的。也就是说多线程情况更新tail节点。
- p != t:多线程情况更新了p节点:p不是尾节点
- (p != t && t != (t = tail)) :如果t还是tail节点或者p节点没有受多线程影响,将p指向t,否则p指向q
-
变化如下
之后,在走到for循环, 执行 Node q = p.next,这里我们知道q=null。 所以,会把新添加的节点设置为 p 的下一个节点:
if (q == null) {
if (p.casNext(null, newNode)) {
if (p != t)
casTail(t, newNode);
return true;
}
}
又因为p!=t,尝试设置tail指向newNode,最后变化如下
3.3、 添加第3个元素,添加成功后,链表如下所示:
3.4、 添加第4个元素,添加成功后,链表如下所示:
3.5、 重点注意
- 上面在添加元素时,每循环两次才会更新一次 tail 节点。
- 为什么不让 tail 节点永远为队列的尾节点,如果让 tail 节点永远为队列的尾节点,则实现的代码会更少且逻辑也会更清晰
- 这是因为,如果让 tail 永远为队列的尾节点,则每次都需要使用循环CAS更新tail节点,如果能减少更新 tail 节点的次数,入队的性能岂不是更高?所以说并不是每次入队都需要更新尾节点,只有在tail节点和尾节点不想等的情况下才更新,这样能减少更新此时,提高效率。
四、 poll方法(获取元素)
-
ConcurrentLinkedQueue是一个FIFO的队列,所以获取元素的时候,总是获取到队列的第一个元素;
-
poll()方法获取元素的时候,返回链表的第一个元素,并删除.
public E poll() { // 循环跳转,goto语法 restartFromHead: for (;;) { // p 表示要出队的节点,默认为 head节点 for (Node<E> h = head, p = h, q;;) { // 出队的元素 E item = p.item; // 如果出队的元素不为空,则把要出队的元素设置null,不更新head节点;如果出队元素为null或者cas设置失败,则表示有其他线程已经进行修改,则需要重写获取 if (item != null && p.casItem(item, null)) { if (p != h) // 当head元素为空,才会更新head节点,这里循环两次,更新一次head节点 updateHead(h, ((q = p.next) != null) ? q : p); // 更新head节点 return item; } // 队列为空,返回null else if ((q = p.next) == null) { updateHead(h, p); return null; } else if (p == q) continue restartFromHead; // 把 p 的next节点赋值给p else p = q; } } }
4.1、 首先队列图
4.2、 获取节点 item0
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
..............................
}
- 标记如下
因为此时 p.item 不为空,所示 CAS 设置 p.item 为null,走第一个if语句,又因为此时 h 节点的 item 不为空,即 p 和 h 相等,所以不会更新头节点,直接返回 p 节点中的元素 item0,如下图所示:
if (item != null && p.casItem(item, null)) {
// 不会执行
if (p != h) //
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
4.2、 接下来再获取 item1,进行标记,如下所示:
因为 p.item 为空, 不执行 if (item != null && p.casItem(item, null)),之后执行 else if ((q = p.next) == null),此时,q指向了 item1。注意,这里先执行q = p.next,也就是说先让q指向了 item1,再来判断是否为null,实际上下一个节点有值。
for (Node<E> h = head, p = h, q;;) {
E item = p.item;
if (item != null && p.casItem(item, null)) {
.......
}
else if ((q = p.next) == null) {
updateHead(h, p);
return null;
}
else if (p == q)
continue restartFromHead;
else
p = q;
}
又因为 q 不为空,所有不会更新head节点,之后会执行最后一个else语句:p = q,第一次循环结束,开始第二次循环,又进行标记,此时标记如下:
因为 p.item 不为空(item1),所以走如下代码逻辑,通过 CAS 把 p.item 设置为null
if (item != null && p.casItem(item, null)) {
if (p != h) // 设置下一节点item2为头结点
updateHead(h, ((q = p.next) != null) ? q : p);
return item;
}
-------
final void updateHead(Node<E> h, Node<E> p) {
if (h != p && casHead(h, p))
h.lazySetNext(h);
}
void lazySetNext(Node<E> val) {
UNSAFE.putOrderedObject(this, nextOffset, val);
}
因为此时,head节点元素为空,即 p 和 h 节点不相等,所以会更新头节点,又因为 p.next 即 item2 不为空,所以把 p.next 即 item2 设置为 head节点,设置成功后,队列如下所示:
4.3、 获取item2也是同样的逻辑
4.4、 获取item3也是同样的逻辑
4.5、 重要
- 从上可知,head节点不一定就是队列的第一个含有元素的节点,也不是每次获取元素后就更新head节点,只有当head中的元素为空的时候才更新head节点,这和添加 offer() 方法中更新tail节点类似,减少 CAS 更新head节点的次数,出队的效率会更高。
五、 验证
package com.feizhou.example.demo;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.AbstractQueue;
import java.util.Iterator;
import java.util.Queue;
import java.util.Spliterator;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
import static java.lang.Thread.currentThread;
public class ConcurrentLinkedQueue2<E> extends AbstractQueue<E> implements Queue<E>, java.io.Serializable {
private static final long serialVersionUID = 196745693267521676L;
private transient volatile Node<E> head;
private transient volatile Node<E> tail;
private static sun.misc.Unsafe UNSAFE;
private static final long headOffset;
private static final long tailOffset;
static {
try {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
try {
UNSAFE = (Unsafe) unsafeField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Class<?> k = ConcurrentLinkedQueue.class;
headOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("head"));
tailOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("tail"));
} catch (Exception e) {
throw new Error(e);
}
}
// 构造
public ConcurrentLinkedQueue2() {
head = tail = new Node<E>(null);
System.out.println("ConcurrentLinkedQueue2----: 创建头结点和为节点" + head);
}
final void updateHead(Node<E> h, Node<E> p) {
System.out.println("updateHead--------------p :" + p);
System.out.println("updateHead--------------h :" + h);
if (h != p && casHead(h, p)) {
System.out.println("updateHead---casHead-->h:" + UNSAFE.getObjectVolatile(this, headOffset));
}
}
@Override
public boolean offer(E e) {
System.out.println("offer---- : 非空校验");
checkNotNull(e);
System.out.println("offer---- : 创建要添加的节点");
final Node<E> newNode = new Node<E>(e);
// 无限循环,入队不成功,则反复入队
// t 表示 tail 节点
// p 表示链表的尾节点,默认等于 tail 节点
for (Node<E> t = tail, p = t; ; ) {
//循环下一个节点
Node<E> q = p.next;
System.out.println("offer---- t:" + t);
System.out.println("offer---- p:" + p);
System.out.println("offer---- q:" + q);
// 如果尾节点的下一个节点为空,则表示 p 为尾节点
if (q == null) {
// CAS 设置尾节点的下一个节点为新添加的节点,如果设置失败,在再次尝试
if (p.casNext(null, newNode)) {
System.out.println("offer---- casNext: true:");
// 如果tail节点有大于等于1个的 next 节点,则更新 tail 节点,将新添加的节点设置为 tail 节点
if (p != t) {// 相当于循环两次更新一次 tail 节点
System.out.println("offer---- casTail: " + casTail(t, newNode));
}
System.out.println("offer---- 结束return true");
return true;
}
} else if (p == q) {// 只有在尾节点和尾节点的下一个节点为空的情况下成立
//多线程操作时候,由于poll操作移除元素后有可能会把head变为自引用,然后head的next变为新head,所以这里需要
//重新找新的head,因为新的head后面的节点才是正常的节点。
System.out.println("offer---- p == q)--》 tail:" + tail);
System.out.println("offer---- p == q)--》 t:" + t);
System.out.println("offer---- p == q--》 p:" + p);
System.out.println("offer---- p == q--》 (t = tail):" + (t = tail));
p = (t != (t = tail)) ? t : head;
} else {
// 把 tail节点设置为为尾节点,再次循环设置下一个节点
System.out.println("offer---- p != q--》 tail:" + tail);
System.out.println("offer---- p != q--》 t:" + t);
System.out.println("offer---- p != q--》 p:" + p);
System.out.println("offer---- p != q--》 p != t:" + (p != t) + "----(t != (t = tail)): " + (t != (t = tail)));
p = (p != t && t != (t = tail)) ? t : q;
}
}
}
@Override
public E poll() {
restartFromHead:
for (; ; ) {
for (Node<E> h = head, p = h, q; ; ) {
E item = p.item;
System.out.println("poll------- p:" + p);
System.out.println("poll------- h:" + h);
System.out.println("poll---- item:" + item);
if (item != null && p.casItem(item, null)) {
System.out.println("poll---- item != null && p.casItem(item, null)--> p :" + p);
System.out.println("poll---- item != null && p.casItem(item, null)--> h :" + h);
if (p != h) {
updateHead(h, ((q = p.next) != null) ? q : p);
}
return item;
} else if ((q = p.next) == null) {
System.out.println("poll---- ((q = p.next) == null)--> q :" + q);
System.out.println("poll---- ((q = p.next) == null)--> p :" + p);
updateHead(h, p);
return null;
} else if (p == q) {
System.out.println("poll---- (p == q)--> q :" + q);
continue restartFromHead;
} else {
System.out.println("poll------- else --> q :" + q);
p = q;
System.out.println("poll------- else --> p :" + p);
}
}
}
}
private static void checkNotNull(Object v) {
if (v == null) {
throw new NullPointerException();
}
}
private boolean casTail(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val);
}
private boolean casHead(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val);
}
@Override
public E peek() {
return null;
}
@Override
public Iterator<E> iterator() {
return null;
}
@Override
public void forEach(Consumer<? super E> action) {
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
return false;
}
@Override
public Spliterator<E> spliterator() {
return null;
}
@Override
public Stream<E> stream() {
return null;
}
@Override
public Stream<E> parallelStream() {
return null;
}
@Override
public int size() {
return 0;
}
private static class Node<E> {
volatile E item;
volatile Node<E> next;
private static sun.misc.Unsafe UNSAFE;
private static final long itemOffset;
private static final long nextOffset;
static {
try {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
try {
UNSAFE = (Unsafe) unsafeField.get(null);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
Class<?> k = Node.class;
itemOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("item"));
nextOffset = UNSAFE.objectFieldOffset
(k.getDeclaredField("next"));
} catch (Exception e) {
throw new Error(e);
}
}
Node(E item) {
UNSAFE.putObject(this, itemOffset, item);
}
boolean casItem(E cmp, E val) {
return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
}
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
", next=" + next +
'}';
}
}
public static void main(String[] args) throws InterruptedException {
System.out.println("main---:当前线程是=" + currentThread());
Thread.sleep(200L);
System.out.println("main休息200毫秒");
ConcurrentLinkedQueue2<String> list = new ConcurrentLinkedQueue2<String>();
// ConcurrentLinkedQueue<String> list = new ConcurrentLinkedQueue<String>();
Runnable run = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("offer 元素 :item" + i);
list.offer("item" + i);
}
}
};
Runnable run2 = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("poll 元素 :" + list.poll());
}
}
};
Thread t1 = new Thread(run);
Thread t2 = new Thread(run2);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
Thread.sleep(1000L);
System.out.println("main休息1000毫秒");
t2.start();
Thread.sleep(200L);
// System.out.println("main休息200毫秒");
// System.out.println("删除 元素 :" + list.remove("offer3"));
}
}
测试结果
main---:当前线程是=Thread[main,5,main]
main休息200毫秒
ConcurrentLinkedQueue2----: 创建头结点和为节点Node{item=null, next=null}
offer 元素 :item0
offer---- : 非空校验
offer---- : 创建要添加的节点
offer---- t:Node{item=null, next=null}
offer---- p:Node{item=null, next=null}
offer---- q:null
offer---- casNext: true:
offer---- 结束return true
offer 元素 :item1
offer---- : 非空校验
offer---- : 创建要添加的节点
offer---- t:Node{item=null, next=Node{item=item0, next=null}}
offer---- p:Node{item=null, next=Node{item=item0, next=null}}
offer---- q:Node{item=item0, next=null}
offer---- p != q--》 tail:Node{item=null, next=Node{item=item0, next=null}}
offer---- p != q--》 t:Node{item=null, next=Node{item=item0, next=null}}
offer---- p != q--》 p:Node{item=null, next=Node{item=item0, next=null}}
offer---- p != q--》 p != t:false----(t != (t = tail)): false
offer---- t:Node{item=null, next=Node{item=item0, next=null}}
offer---- p:Node{item=item0, next=null}
offer---- q:null
offer---- casNext: true:
offer---- casTail: true
offer---- 结束return true
offer 元素 :item2
offer---- : 非空校验
offer---- : 创建要添加的节点
offer---- t:Node{item=item1, next=null}
offer---- p:Node{item=item1, next=null}
offer---- q:null
offer---- casNext: true:
offer---- 结束return true
offer 元素 :item3
offer---- : 非空校验
offer---- : 创建要添加的节点
offer---- t:Node{item=item1, next=Node{item=item2, next=null}}
offer---- p:Node{item=item1, next=Node{item=item2, next=null}}
offer---- q:Node{item=item2, next=null}
offer---- p != q--》 tail:Node{item=item1, next=Node{item=item2, next=null}}
offer---- p != q--》 t:Node{item=item1, next=Node{item=item2, next=null}}
offer---- p != q--》 p:Node{item=item1, next=Node{item=item2, next=null}}
offer---- p != q--》 p != t:false----(t != (t = tail)): false
offer---- t:Node{item=item1, next=Node{item=item2, next=null}}
offer---- p:Node{item=item2, next=null}
offer---- q:null
offer---- casNext: true:
offer---- casTail: true
offer---- 结束return true
offer 元素 :item4
offer---- : 非空校验
offer---- : 创建要添加的节点
offer---- t:Node{item=item3, next=null}
offer---- p:Node{item=item3, next=null}
offer---- q:null
offer---- casNext: true:
offer---- 结束return true
main休息1000毫秒
poll------- p:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
poll------- h:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
poll---- item:null
poll------- else --> q :Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
poll------- else --> p :Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
poll------- p:Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
poll------- h:Node{item=null, next=Node{item=item0, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
poll---- item:item0
poll---- item != null && p.casItem(item, null)--> p :Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}
poll---- item != null && p.casItem(item, null)--> h :Node{item=null, next=Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
updateHead--------------p :Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
updateHead--------------h :Node{item=null, next=Node{item=null, next=Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}}}
updateHead---casHead-->h:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll 元素 :item0
poll------- p:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll------- h:Node{item=item1, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll---- item:item1
poll---- item != null && p.casItem(item, null)--> p :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll---- item != null && p.casItem(item, null)--> h :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll 元素 :item1
poll------- p:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll------- h:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll---- item:null
poll------- else --> q :Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}
poll------- else --> p :Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll------- p:Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}
poll------- h:Node{item=null, next=Node{item=item2, next=Node{item=item3, next=Node{item=item4, next=null}}}}
poll---- item:item2
poll---- item != null && p.casItem(item, null)--> p :Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}
poll---- item != null && p.casItem(item, null)--> h :Node{item=null, next=Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}}
updateHead--------------p :Node{item=item3, next=Node{item=item4, next=null}}
updateHead--------------h :Node{item=null, next=Node{item=null, next=Node{item=item3, next=Node{item=item4, next=null}}}}
updateHead---casHead-->h:Node{item=item3, next=Node{item=item4, next=null}}
poll 元素 :item2
poll------- p:Node{item=item3, next=Node{item=item4, next=null}}
poll------- h:Node{item=item3, next=Node{item=item4, next=null}}
poll---- item:item3
poll---- item != null && p.casItem(item, null)--> p :Node{item=null, next=Node{item=item4, next=null}}
poll---- item != null && p.casItem(item, null)--> h :Node{item=null, next=Node{item=item4, next=null}}
poll 元素 :item3
poll------- p:Node{item=null, next=Node{item=item4, next=null}}
poll------- h:Node{item=null, next=Node{item=item4, next=null}}
poll---- item:null
poll------- else --> q :Node{item=item4, next=null}
poll------- else --> p :Node{item=null, next=Node{item=item4, next=null}}
poll------- p:Node{item=item4, next=null}
poll------- h:Node{item=null, next=Node{item=item4, next=null}}
poll---- item:item4
poll---- item != null && p.casItem(item, null)--> p :Node{item=null, next=null}
poll---- item != null && p.casItem(item, null)--> h :Node{item=null, next=Node{item=null, next=null}}
updateHead--------------p :Node{item=null, next=null}
updateHead--------------h :Node{item=null, next=Node{item=null, next=null}}
updateHead---casHead-->h:Node{item=null, next=null}
poll 元素 :item4