You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何AbstractQueuedSynchronizer的cancelAcquire()要将node.next设为自身?

关于AQS中cancelAcquire()方法里node.next = node的疑问

我在阅读AbstractQueuedSynchronizer(AQS)源码时,对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为什么不直接将node.next设为null?这样同样能帮助垃圾回收,希望有人能解答我的疑问。


解答

将node.next设为自身而非null,核心是为了避免队列遍历逻辑出现异常,同时确保GC能正常回收节点,具体原因如下:

  1. 避免遍历逻辑中断或空指针
    AQS内部存在不少遍历队列的逻辑(比如寻找下一个需要唤醒的节点),这些逻辑会沿着节点的next指针遍历。如果将已取消节点的next设为null,遍历到这个节点时会直接终止,导致后续的有效节点无法被处理。而将next指向自身后,遍历逻辑可以通过判断next == node识别出这是一个已取消的节点,从而正确跳过它,继续处理后续节点。

  2. 确保GC正常回收的同时避免残留引用问题
    HotSpot虚拟机的垃圾回收器可以识别循环引用并回收对应的对象,所以node.next = node这种自引用不会阻止GC回收该节点。相反,如果设为null,若有其他线程临时持有该节点的引用,后续访问next时可能触发空指针异常;而自引用的方式可以避免这种情况,同时彻底切断该节点与队列有效链的联系。

  3. 符合AQS的节点状态约定
    AQS中已取消的节点需要被彻底从队列的有效链路中移除,将next指向自身是一种明确的标记,告诉其他线程该节点已经作废,无需再处理它的后续节点,这是AQS内部统一的节点作废标记方式。


内容的提问来源于stack exchange,提问作者Tom Chen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:58:17