关于 ConcurrentLinkedQueue 的一些疑问

2021-10-12 11:55:52 +08:00
 GiftedJarvis

关于 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 的值不一样了?

1783 次点击
所在节点    Java
5 条回复
XYxe
2021-10-12 12:44:08 +08:00
GiftedJarvis
2021-10-12 13:55:36 +08:00
@XYxe 十分感谢,困扰两天了,根本没往 IDEA 的问题上面想
Chinsung
2021-10-12 17:18:20 +08:00
@GiftedJarvis 之前遇到过一个类似问题,某个很 SB 的框架类在 toString 里面修改了对象的值,debug 阶段 idea 会调用 toString 以输出对象的 view,然后 debug 半天一直发现值和预想的不一样。
huang119412
2021-10-13 10:44:27 +08:00
老问题了 https://juejin.cn/post/6844904177437523982 不仅 idea 有问题
GiftedJarvis
2021-10-14 17:35:18 +08:00
各位大佬, 关闭 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), 但代码可读性真的降低了好多呀

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/807258

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX