V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
GiftedJarvis
V2EX  ›  Java

关于 ConcurrentLinkedQueue 的一些疑问

  •  1
     
  •   GiftedJarvis · 46 天前 · 879 次点击
    这是一个创建于 46 天前的主题,其中的信息可能已经有所发展或是发生改变。

    关于 ConcurrentLinkedQueue 的一些疑问

    private transient volatile Node<E> head;
    
    private transient volatile Node<E> tail;
    
    public ConcurrentLinkedQueue() {
        head = tail = new Node<E>(null);
    }
    
    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;
                }
            }
            else if (p == q)
                p = (t != (t = tail)) ? t : head;
            else
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }
    
    boolean casNext(Node<E> cmp, Node<E> val) {
        return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
    }
    

    前提

    使用 ConcurrentLinkedQueue() 构造一个对象,并首次调用 offer() 方法

    问题

    当代码执行到 if (q == null) 时,head == tail,但是当执行完 p.casNext(null, newNode),为什么是 head 成为了 { item = 1, next = null },也就是 newNode,而 tail 变成了 { item = null, next = tail ( tail 本身) } ? p 是从 t 赋值来的,而 t 是从 tail 赋值来的,tail == head,为什么调用 p.casNext(null, newNode) 会同时改变 head 和 tail 的值,且 head 和 tail 的值不一样了?

    5 条回复    2021-10-14 17:35:18 +08:00
    XYxe
        1
    XYxe   46 天前   ❤️ 1
    GiftedJarvis
        2
    GiftedJarvis   46 天前
    @XYxe 十分感谢,困扰两天了,根本没往 IDEA 的问题上面想
    Chinsung
        3
    Chinsung   46 天前   ❤️ 1
    @GiftedJarvis 之前遇到过一个类似问题,某个很 SB 的框架类在 toString 里面修改了对象的值,debug 阶段 idea 会调用 toString 以输出对象的 view,然后 debug 半天一直发现值和预想的不一样。
    huang119412
        4
    huang119412   45 天前
    老问题了 https://juejin.cn/post/6844904177437523982 不仅 idea 有问题
    GiftedJarvis
        5
    GiftedJarvis   44 天前
    各位大佬, 关闭 IDEA Debugger toString 后确实解决问题了, 这是新的结果:

    初始化
    head { item = null, next = null }
    tail { item = null, next = null }

    第一次添加值完成后
    head { item = null, next = { item = 1, next = null } }
    tail { item = null, next = { item = 1, next = null } }

    第二次添加值完成后
    head { item = null, next = { item = 1, next = { item = 2, next = null } } }
    tail { item = 2, next = null }

    第三次添加值完成后
    head { item = null, next = { item = 1, next = { item = 2, next = { item = 3, next = null } } } }
    tail { item = 2, next = { item = 3, next = null } }

    第四次添加值完成后
    head { item = null, next = { item = 2, next = { item = 3, next = { item = 4, next = null } } } }
    tail { item = 4, next = null }

    然后, 有了新的疑问, 为什么要这样设计呢?
    tail 并不一定是尾结点, 为此还需要重新确定尾结点, 难道是因为性能吗? 这样可以少进行一次 casTail(t, newNode), 但代码可读性真的降低了好多呀
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1208 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 18:45 · PVG 02:45 · LAX 10:45 · JFK 13:45
    ♥ Do have faith in what you're doing.