前言: 本渣渣想分析分析Doug Lea大佬对高并发代码编写思路, 于是找到了我们今天的小主角ConcurrentLinkedQueue进行鞭打, 说实话草稿我都打好了, 就差临门一脚, 给踢折了

直接看问题, ideaDebug非Debug模式下运行结果不同, vscode复现, eclipse毫无鸭梨

怎么发现的问题?

从这段代码开始

public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {    ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();    queue.add("zhazha");    // 在下面这行下断点    Field headField = queue.getClass().getDeclaredField("head");    headField.setAccessible(true);    Object head = headField.get(queue);        Field itemField = queue.getClass().getDeclaredField("ITEM");    itemField.setAccessible(true);    VarHandle ITEM = (VarHandle) itemField.get(head);    Object o = ITEM.get(head);    System.out.println(o);}

你会发现一个神奇的现象, 如果我们下断点在Field headField = queue.getClass().getDeclaredField("head");这一行代码, 单步执行下来会发现System.out.println(o);打印出了zhazha, 但是如果不下断点, 直接运行打印null

为了防止是WARNING: An illegal reflective access operation has occurred警告的影响, 我改了改源码, 用unsafe获取试试
private static Unsafe unsafe;static {    Class<Unsafe> unsafeClass = Unsafe.class;    Unsafe unsafe = null;    try {        Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");        unsafeField.setAccessible(true);        ConcurrentLinkedQueueDemo.unsafe = (Unsafe) unsafeField.get(null);    } catch (NoSuchFieldException | IllegalAccessException e) {        e.printStackTrace();    }}public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {    ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();    queue.add("zhazha");    // 在下面这行下断点    long headOffset = unsafe.objectFieldOffset(queue.getClass().getDeclaredField("head"));    Object head = unsafe.getObject(queue, headOffset);        long itemOffset = unsafe.staticFieldOffset(ConcurrentLinkedQueue.class.getDeclaredField("ITEM"));    Object base = unsafe.staticFieldBase(ConcurrentLinkedQueue.class.getDeclaredField("ITEM"));    VarHandle ITEM = (VarHandle) unsafe.getObject(base, itemOffset);        Object o = ITEM.get(head);    System.out.println(o);}

完美复现

第一反应我的问题

去源码里看看怎么回事. 但.......这...........

仔细看红箭头的地址, tpheadtail都是同一个地址, 看上面的代码发现全是tail赋值给这三个变量的
NEXT源码

他的接收类是Node, 接收字段是next, 接收字段类型Node

看这源码的势头, NEXT修改的是p对象, 如果该对象的next节点为null, 则把newNode设置到节点上, 此时p对象指向的是tail, 同时head也是指向的tail节点, 所以这句话执行完毕, head.nexttail.next同样都是newNode节点
但.....................这.....................

head节点被直接替换掉, tail保持不变

此时我的表情应该是这样

怀疑猫生

private static Unsafe unsafe;static {    Class<Unsafe> unsafeClass = Unsafe.class;    Unsafe unsafe = null;    try {        Field unsafeField = unsafeClass.getDeclaredField("theUnsafe");        unsafeField.setAccessible(true);        ConcurrentLinkedQueueDemo.unsafe = (Unsafe) unsafeField.get(null);    } catch (NoSuchFieldException | IllegalAccessException e) {        e.printStackTrace();    }}public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {    ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();    queue.add("zhazha");        // 在这里下断点    Class<? extends ConcurrentLinkedQueue> queueClass = queue.getClass();        Object head = unsafe.getObject(queue, unsafe.objectFieldOffset(queueClass.getDeclaredField("head")));        Field itemField = queueClass.getDeclaredField("ITEM");    itemField.setAccessible(true);    VarHandle ITEM = (VarHandle) itemField.get(queue);    Object item = ITEM.get(head);    System.out.println(item); // zhazha            long itemOffset = unsafe.staticFieldOffset(queueClass.getDeclaredField("ITEM"));    Object base = unsafe.staticFieldBase(queueClass.getDeclaredField("ITEM"));    VarHandle ITEM2 = (VarHandle) unsafe.getObject(base, itemOffset);    Object item2 = ITEM2.get(head);    System.out.println(item2); // zhazha}

单步调试出来还是zhazha, 而且为了防止反射出了问题, 我同时用了Unsafe和反射两种方法

copy 源码添加自己的调试函数再次测试

得了得了, 放终极大招试试, copy ConcurrentLinkedQueue源码出来改成MyConcurrentLinkedQueue
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) {            if (NEXT.compareAndSet(p, null, newNode)) {                System.out.println("this.head.item = " + this.head.item);                System.out.println("this.tail.item = " + this.tail.item);                System.out.println("this.head.next.item = " + this.head.next.item);                System.out.println("this.tail.next.item = " + this.tail.next.item);                if (p != t) {                    TAIL.weakCompareAndSet(this, t, newNode);                }                return true;            }        }        else if (p == q) {            p = (t != (t = tail)) ? t : head;        }        else {            p = (p != t && t != (t = tail)) ? t : q;        }    }}

主函数就比较简单了直接

public static void main(String[] args) {    MyConcurrentLinkedQueue<String> queue = new MyConcurrentLinkedQueue<String>();    queue.add("zhazha");}

直接在非Debug模式下运行, 发现打印出来的是

this.head.item = nullthis.tail.item = nullthis.head.next.item = zhazhathis.tail.next.item = zhazhaProcess finished with exit code 0

Debug模式下单步运行发现

this.head.item = zhazhathis.tail.item = nullException in thread "main" java.lang.NullPointerException    at com.zhazha.juc.MyConcurrentLinkedQueue.offer(MyConcurrentLinkedQueue.java:117)    at com.zhazha.juc.MyConcurrentLinkedQueue.add(MyConcurrentLinkedQueue.java:67)    at com.zhazha.juc.MyConcurrentLinkedQueueDemo.main(MyConcurrentLinkedQueueDemo.java:13)Process finished with exit code 1

纳尼?

不信邪的我在NEXT cas操作的前后增加了sleep方法, 以非Debug模式下运行

this.head.item = nullthis.tail.item = nullthis.head.next.item = zhazhathis.tail.next.item = zhazha

还是不一样

多环境IDE测试

放终极终极终极SVIP大招 ===> 放在eclipse上试试??? 或者vscode上???

在vscode上以Debug模式单步运行输出

this.head.item = zhazhathis.tail.item = nullException in thread "main" java.lang.NullPointerException        at MyConcurrentLinkedQueue.offer(MyConcurrentLinkedQueue.java:116)        at MyConcurrentLinkedQueue.add(MyConcurrentLinkedQueue.java:66)        at MyConcurrentLinkedQueueDemo.main(MyConcurrentLinkedQueueDemo.java:11)

非Debug模式直接输出

this.head.item = nullthis.tail.item = nullthis.head.next.item = zhazhathis.tail.next.item = zhazha

在eclipse上以Debug模式单步运行输出

this.head.item = nullthis.tail.item = nullthis.head.next.item = zhazhathis.tail.next.item = zhazha

非Debug运行输出

this.head.item = nullthis.tail.item = nullthis.head.next.item = zhazhathis.tail.next.item = zhazha

发现了没有? 还是我大eclipse坚挺住了