一、常见问题
二、初始化
以链表方式实现并发队列,对链表的操作全部使用VarHandle替代jdk1.8的Unsafe,变量句柄(Variable Handles)目的是定义一个标准的用法来操作对象字段和数组元素,等效于java.util.concurrent.atomic 和 sun.misc.Unsafe。并且提供了一系列标准的内存屏障操作,用于更加细粒度的指令重排序。在安全性、可用性、性能上都要优于现有的API
private static final VarHandle HEAD;//用来全局变量标记队列头元素head的原子操作
private static final VarHandle TAIL;//用来全局变量标记队尾元素head的原子操作
static final VarHandle ITEM;//用来对节点的值的原子操作
static final VarHandle NEXT;//用来对节点的next值的原子操作
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
HEAD = l.findVarHandle(ConcurrentLinkedQueue.class, "head",Node.class);//取全局变量head,类型为Node
TAIL = l.findVarHandle(ConcurrentLinkedQueue.class, "tail",Node.class);//取全局变量tail,类型为Node
ITEM = l.findVarHandle(Node.class, "item", Object.class);//取Node类里的变量item,类型为object
NEXT = l.findVarHandle(Node.class, "next", Node.class);//取Node类里的变量next,类型为Node
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
构造方法
public ConcurrentLinkedQueue() { head = tail = new Node<E>(); }
,默认指向一个空节点,因此新节点的值不能为null
Node源码
static final class Node<E> {
volatile E item;
volatile Node<E> next;
Node(E item) {
ITEM.set(this, item);
}
Node() {}
void appendRelaxed(Node<E> next) {
NEXT.set(this, next);
}
boolean casItem(E cmp, E val) {
return ITEM.compareAndSet(this, cmp, val);
}
}
offer 流程
public boolean offer(E e) {
final Node<E> newNode = new Node<E>(Objects.requireNonNull(e));
for (Node<E> t = tail, p = t; ; ) {
Node<E> q = p.next;
//表示当前节点为最后的节点
if (q == null) {
// p is last node
if (NEXT.compareAndSet(p, null, newNode)) {
// Successful CAS is the linearization point
// for e to become an element of this queue,
// and for newNode to become "live".
//插入成功后,再次判断,tail是否已经改变,如果p != tail,则尝试将newNode设置为Tail,设置失败也没关系,因为有其它线程设置了
//p被认为队列真正的尾节点,tail不一定指向对象真正的尾节点,因为在ConcurrentLinkedQueue中tail是被延迟更新的
if (p != t) // hop two nodes at a time; failure is OK
TAIL.weakCompareAndSet(this, t, newNode);
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.
//从head重新开始肯定正确,但是如果tail指针有更新过,那么从tail开始大概率可能效率更高。
p = (t != (t = tail)) ? t : head;
else
// Check for tail updates after two hops.
// 按道理直接p=q跳到下一个节点就Ok了,但是代码里面做了这个判断(p != t && t != (t = tail)),如果p已经正常往后移动了一次,且tail发生了变化,那么从新的tail重新开始。为什么要加个(p!=t)的前置判断呢?我认为是为了提高效率,因为tail是valotile变量,读取有一定代价,当p==t的时候,作者认为再往后跳一下成功的概率挺高。(作者应该经过测量,可见对性能的压榨已经丧心病狂了)。
p = (p != t && t != (t = tail)) ? t : q;
}
}