ConcurrentLinkedQueue源码注解和常见问题总结

一、常见问题

二、初始化

以链表方式实现并发队列,对链表的操作全部使用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;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值