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

Java ReentrantLock的unlock方法未重置队列头,已执行线程节点如何移除?

你的理解偏差在于:AQS 队列的出队操作不由释放锁的release逻辑负责,而是由被唤醒后成功抢到锁的后继线程在锁获取流程中执行的。

核心逻辑梳理

AQS 的等待队列中,头节点默认代表已经持有锁的线程对应的节点,出队操作发生在acquireQueued方法的抢锁成功分支,对应核心源码如下:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            // 前驱是头节点时尝试抢锁
            if (p == head && tryAcquire(arg)) {
                // 抢锁成功后将自身设为新的头节点,完成旧头节点出队
                setHead(node);
                p.next = null; // 移除旧头节点的引用,帮助GC回收
                failed = false;
                return interrupted;
            }
            // 抢锁失败则挂起等待唤醒
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

其中setHead方法会直接重置队列头,切断旧头节点的引用:

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

完整流程说明

  1. 持有锁的线程调用unlock触发release逻辑,释放锁资源后唤醒头节点的后继等待线程
  2. 被唤醒的线程从挂起状态恢复,继续执行acquireQueued中的循环抢锁逻辑
  3. 抢锁成功后执行setHead将自身设置为新的头节点,旧头节点因为没有任何可达引用会被GC回收

这种设计的优势是将队列修改逻辑收敛到抢锁成功的单线程中,不需要额外的并发控制,也降低了释放锁线程的执行开销,队列长度始终等于当前等待锁的线程数+1个当前头节点,不会出现无限增长的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 00:21:01