ConcurrentLinkedQueue基于JDK1.8源码学习心得

最近在读《Java并发编程的艺术》,书中关于ConcurrentLinkedQueue的描述并不是基于JDK1.8

相较于JDK1.8版本的,还是修改了挺多地方的,比如关于HOPS的设计意图直接去掉了

参考贴:https://www.jianshu.com/p/08e8b0c424c0
https://blog.csdn.net/lejustdoit/article/details/98477819?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase

大部分东西上面两篇博客已经讲的很清楚了,这里补充一点自己的学习时候没清楚的点

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) {
             if (p.casNext(null, newNode)) {
                 if (p != t)
                     casTail(t, newNode);
                 return true;
             }//1CAS自旋失败
         }
         else if (p == q)//2某个线程提前出队节点导致p=q,详情见第一篇参考贴
             p = (t != (t = tail)) ? t : head;
         else//3让p指针始终指向尾节点
             p = (p != t && t != (t = tail)) ? t : q;
    }
}

1、注解1,2,3都是当前节点插入失效,需要重新插入,2考虑了tail滞后于head的情况(节点出队,详情见第一篇参考贴)

注解3和4中的t=tail这步操作很神奇,它有效的减少了CAS更新tail节点的次数,这也是曾经的HOPS的设计意图

t=tail跟CAS相比有线程安全问题,但是这里对线程安全的要求并不高,因为判断是否真的是尾节点在if (q == null),所以它只是象征性的更新一下尾节点

若刚好更新成为尾节点,那么CAS更新casTail(t, newNode)这步不就不用做了

若运气不好没有更新尾节点,那么并不影响下次循环

2、p = (t != (t = tail)) ? t : head;

这里比较的是地址是否相同,而不是值

刚开始以为一直是true,先执行t=tail,然后判断t!=t,debug的时候发现不是

首先判断t的地址是否是tail,然后才是t=taill,测试代码如下

class No {
    int k, v;
    public No(int k, int v) {
        this.k = k;
        this.v = v;
    }
}
No B = new No(3, 4);
No A = B;//当A=B时返回1 2
//No A = new No(1, 9);//当A!=B时返回3 4
No C = new No(1, 2);
No D = (A!=(A=B))?A:C;
System.out.println(D.k + " " + D.v);

然后还有一句这个,p = (p != t && t != (t = tail)) ? t : q;

根据&&的特性,如果p=t那么&&后的语句将不会被执行,也就是说t最后没有被赋值

3、可以发现程序当前已经经过casNext方法,可以发现此时p指针并没有指向新节点newNode

ConcurrentLinkedQueue基于JDK1.8源码学习心得

相关推荐