导读:本期聚焦于小伙伴创作的《如何通过 AQS 的 Condition 队列源码分析线程从等待状态转移到同步队列的完整原子过程》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何通过 AQS 的 Condition 队列源码分析线程从等待状态转移到同步队列的完整原子过程》有用,将其分享出去将是对创作者最好的鼓励。

AQS的Condition是基于Lock实现的线程等待通知组件,每个Condition实例都关联一个独立的等待队列,线程调用await方法后会进入该队列等待,被signal唤醒后需要转移到AQS的同步队列中重新竞争锁,整个过程需要保证操作的原子性,避免线程状态错乱。

如何通过 AQS 的 Condition 队列源码分析线程从等待状态转移到同步队列的完整原子过程

Condition队列的基本结构

Condition队列是AQS内部维护的一个单向链表,每个节点复用了AQS同步队列的Node类,不过Condition队列的节点只有nextWaiter指针,没有prev和next指针,队列的头节点是firstWaiter,尾节点是lastWaiter。线程调用lock.newCondition()创建的Condition实例,内部持有这个队列的引用。

线程进入等待队列的流程

当线程持有锁的情况下调用Condition的await方法时,首先会将当前线程封装成Node节点加入Condition队列的尾部,然后释放当前持有的锁,之后循环检查自己是否在同步队列中,如果不在就挂起线程。

以下是await方法的核心源码逻辑:

public final void await() throws InterruptedException {
    // 响应中断检查
    if (Thread.interrupted())
        throw new InterruptedException();
    // 将当前线程加入Condition等待队列
    Node node = addConditionWaiter();
    // 释放当前持有的锁,返回释放前的锁状态
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // 循环检查节点是否在同步队列中,不在就挂起
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // 进入同步队列后竞争锁
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    // 清理取消的节点
    if (node.nextWaiter != null)
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}

// 添加节点到Condition队列尾部
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // 如果尾节点被取消,清理所有取消的节点
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    // 创建新的等待节点,状态为CONDITION
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
    lastWaiter = node;
    return node;
}

线程被唤醒后转移到同步队列的原子过程

当其他线程调用Condition的signal方法时,会将Condition队列的头节点转移到同步队列中,这个转移过程需要保证原子性,避免多个线程同时操作节点导致状态不一致。

signal方法的核心逻辑是先获取Condition队列的头节点,然后调用transferForSignal方法完成转移,以下是相关源码:

public final void signal() {
    // 检查当前线程是否持有锁
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
        doSignal(first);
}

private void doSignal(Node first) {
    do {
        // 移除头节点,将firstWaiter指向下一个节点
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}

// 将节点从Condition队列转移到同步队列,返回是否成功
final boolean transferForSignal(Node node) {
    // 原子修改节点状态,从CONDITION改为0,失败说明节点已经被取消
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;
    // 将节点加入同步队列尾部,返回前驱节点
    Node p = enq(node);
    int ws = p.waitStatus;
    // 如果前驱节点被取消,或者修改前驱节点状态为SIGNAL失败,就唤醒当前节点线程
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

transferForSignal方法是整个转移过程的核心,首先通过CAS操作将节点的waitStatus从CONDITION改为0,这一步是原子操作,保证只有被唤醒的节点能完成状态变更。如果CAS失败,说明节点已经被取消,直接返回false,doSignal会继续处理下一个节点。CAS成功后,调用enq方法将节点加入同步队列的尾部,enq方法内部也是通过自旋加CAS的方式保证入队操作的原子性。最后修改前驱节点的状态为SIGNAL,确保前驱节点释放锁时能唤醒当前节点,如果修改失败就直接唤醒当前节点,避免线程一直挂起。

转移过程的原子性保证

整个转移过程的原子性主要通过两点实现:一是节点状态变更的CAS操作,保证同一个节点不会被多次转移;二是同步队列入队的enq方法通过自旋CAS保证入队操作的原子性,避免多个线程同时入队导致队列结构错乱。同时signal方法要求调用线程必须持有锁,保证了同一时间只有一个线程执行唤醒操作,进一步避免了并发问题。

特殊情况处理

如果线程在等待过程中被中断,await方法会先检查中断状态,若中断发生在signal之前,会将中断模式设置为THROW_IE,之后转移节点到同步队列后抛出中断异常;若中断发生在signal之后,会将中断模式设置为REINTERRUPT,之后重新设置中断标志。如果节点在转移前被取消,CAS修改状态会失败,该节点不会被转移到同步队列,后续会被清理出Condition队列。

AQSCondition_队列线程等待同步队列原子过程修改时间:2026-06-10 06:21:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。