java 为什么AbstractQueuedSynchronizer的cancelAcquire()设置node.next =node?

dm7nw8vv  于 2023-05-21  发布在  Java
关注(0)|答案(2)|浏览(155)

我正在阅读AbstractQueuedSynchronizer的源代码。我发现cancelAcquire()方法有点难以理解,最后一行混淆了我:

private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // Skip cancelled predecessors
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // predNext is the apparent node to unsplice. CASes below will
        // fail if not, in which case, we lost race vs another cancel
        // or signal, so no further action is necessary, although with
        // a possibility that a cancelled node may transiently remain
        // reachable.
        Node predNext = pred.next;

        // Can use unconditional write instead of CAS here.
        // After this atomic step, other Nodes can skip past us.
        // Before, we are free of interference from other threads.
        node.waitStatus = Node.CANCELLED;

        // If we are the tail, remove ourselves.
        if (node == tail && compareAndSetTail(node, pred)) {
            pred.compareAndSetNext(predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    pred.compareAndSetNext(predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC  !!!!CONFUSE ME!!!!
        }
    }

正如你所看到的,最后一行让我困惑:

node.next = node; // help GC

为什么不直接设置为null:node.next =null,它也帮助gc。有人能帮忙吗?谢谢!

p1tboqfb

p1tboqfb1#

“node.next = node;”行用于帮助垃圾收集器(GC)打破链表中的潜在循环。如果“node.next”被设置为“null”,则它将在先前节点pred的next字段中留下对节点的引用,这将防止节点被垃圾收集,即使在它已经从列表中移除之后。相反,通过将“node.next”设置为node,它创建了一个自循环,该自循环作为一个明确的标记,表明该节点已从列表中删除,并且任何其他节点都无法再访问。当GC遇到自循环时,它知道可以安全地对对象进行垃圾收集。
如果node.next系统中存在对node的其他引用,则将www.example.com设置为null不足以打破循环。相反,将node.next设置为node会向GC提供一个更强的信号,即node不再使用,因为它创建了一个返回到node的唯一引用,该引用不能被任何其他对象共享。

b09cbbtk

b09cbbtk2#

这是因为跨代引用,也就是说,当旧代中的对象引用它时,年轻代中的对象不能被收集。
请考虑一下,当一个节点进入旧的gen,后来它出队了,但是你没有把它的next指针设置为它自己或null。然后,当下一个节点被出队时,即使它不可达,它仍然不能被次要GC回收,因为它是由旧生成中的节点链接的。而且情况会越来越糟,直到执行完整的GC,因为它将同时查找年轻的gen和旧的gen,并收集所有无法访问的对象。
您可以从第23分钟开始检查this video,这说明了我上面提到的内容。
现在我们知道,在下一个节点出列之前,有必要切断与它的连接。但是为什么要将node.next设置为自身而不是null呢?这是因为node.nextnull值具有特殊含义,该节点位于队列的末尾。
正如对属性java.util.concurrent.locks.AbstractQueuedSynchronizer.Node#next的评论所述:

  • 已取消节点的下一个字段被设置为指向节点本身而不是null,以使isOnSyncQueue的工作更轻松。*
    **基本上,这都是因为垃圾收集器的工作方式。**当旧代中链接到obj1的obj2可访问时,如果次要GC不收集年轻代中的obj1,这是可以的。因为我们还在使用obj2。但是当obj2不可访问时,如果次要GC仍然保留obj1,这将导致麻烦,这需要我们了解GC的这些细节以防止这种情况发生。

相关问题