Java AQS核心源码解析:同步器架构与锁获取机制

苏见山
苏见山
发布时间:2026-10-16 | 5人浏览过
原创

AQS (AbstractQueuedSynchronizer) 是 JDK 中 ReentrantLock、CountDownLatch 等同步组件的底层基石。理解其内部的状态流转、双向链表结构以及加锁/唤醒机制,是深入掌握 Java 并发编程的关键。

1、整体架构设计

AQS 本质上是锁框架的基础设施,它定义了一套标准的获取锁和释放锁的逻辑模板。任何想要实现自定义同步器的类,只需继承 AQS 并实现少量抽象方法即可。

该架构图展示了 AQS 的核心组成及不同场景下的动态流向:

  • 双队列结构: AQS 维护了两个队列,分别是同步队列(CLH变体)用于管理等待获取锁的线程,以及条件队列用于配合 Condition 对象进行线程间通信。两者底层均为双向链表。
  • 运行场景: 图中不同颜色的线条代表了排队、尝试获取锁、阻塞、被唤醒等不同阶段的流转逻辑。

1.1、核心属性与数据结构

要理解 AQS 的工作原理,首先需要明确其几个关键的属性定义:

状态字段 (state)

state 是同步器的核心状态标识,通常用来表示资源是否可用。例如,在可重入锁中,state 代表锁的重入次数;在 CountDownLatch 中,state 代表剩余计数值。修改该字段需要使用 volatile 保证可见性,并通过 CAS 操作保证原子性。

// 同步器的核心状态
private volatile int state;

同步队列头尾指针

当多个线程竞争锁失败时,会被封装成 Node 节点放入同步队列中排队阻塞。AQS 通过两个 volatile 指针管理这个双向链表:

// 同步队列的头节点
private transient volatile Node head;
// 同步队列的尾节点
private transient volatile Node tail;

节点结构 (Node)

Node 是同步队列中的基本元素,它缓存了当前线程、节点状态以及前后指针关系。其中最值得关注的是 waitStatus 字段,它控制着节点的行为:

static final class Node {
    // 节点等待状态
    // SIGNAL (-1): 表示后继节点需要被唤醒
    // CANCELLED (1): 节点已取消
    // CONDITION (-2): 节点处于条件队列
    // PROPAGATE (-3): 共享模式下下一次 acquires 应当无条件传播
    volatile int waitStatus;
    
    volatile Node prev;  // 前驱节点
    volatile Node next;  // 后继节点
    volatile Thread thread; // 当前线程
}

条件队列 (ConditionObject)

条件队列同样是链表结构,但与同步队列不直接交互。它主要用于实现类似 Object.wait()/notify() 的高级线程协作功能。通过调用 new ConditionObject() 可创建一个条件对象。一个 Lock 可以绑定多个不同的 Condition,从而实现更精细的线程通知策略(如生产者-消费者模型中的“满”和“空”条件)。

2、同步器状态辨析

在阅读 AQS 源码时,务必区分两种状态:

  • state: 全局状态,由 AQS 子类(如锁的实现)使用,用于判断资源占用情况。
  • waitStatus: 节点状态,由 AQS 内部管理,用于控制线程的阻塞、唤醒或取消行为。

3、获取锁机制详解

获取锁的过程主要由 acquire 系列方法驱动。无论排他锁还是共享锁,其核心思想都是:先尝试直接获取,失败则进入队列排队等待。

3.1、独占模式获取锁 (acquire)

以 ReentrantLock 为例,调用 lock.lock() 最终会触发 AQS 的 acquire(arg) 方法:

public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

该方法包含三个清晰的步骤:

  1. 尝试获取: 调用 tryAcquire(arg)。这是留给子类实现的模板方法,通过 CAS 修改 state。如果成功,直接返回。
  2. 包装入队: 如果获取失败,调用 addWaiter(Node.EXCLUSIVE) 将当前线程包装为独占模式的 Node,并尝试追加到同步队列尾部。
  3. 排队阻塞: 调用 acquireQueued。这是一个自旋过程,节点会在队列中反复尝试获取锁,并在适当时候挂起(park),直到拿到锁或被中断。

核心细节:addWaiter 与 enq

为了优化性能,addWaiter 采用了一次非阻塞的 CAS 尝试,如果成功则快速返回;只有失败时才进入 enq 方法进行无限循环重试,确保节点最终能加入队尾。

核心细节:acquireQueued 与 挂起逻辑

acquireQueued 是整个 AQS 最复杂的环节之一,它保证了线程安全地阻塞和唤醒:

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            // 如果前驱是头节点,说明有机会获取锁
            if (p == head && tryAcquire(arg)) {
                setHead(node); // 把自己设为头节点
                p.next = null; // 回收旧头节点
                failed = false;
                return interrupted;
            }
            // 检查是否需要挂起
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

其中 shouldParkAfterFailedAcquire 负责处理前驱节点的 waitStatus:

  • 如果前驱状态为 SIGNAL,说明可以放心挂起当前节点。
  • 如果前驱状态为 CANCELLED (>0),则向前查找有效节点并将当前节点链接过去。
  • 否则,通过 CAS 将前驱状态改为 SIGNAL,返回 false 促使外层循环再次尝试(因为此时还不能挂起)。

3.2、共享模式获取锁 (acquireShared)

共享锁(如 ReadWriteLock 的读锁)允许同时存在多个持有者。其入口为 acquireShared(arg),逻辑框架与独占锁类似,但在获得锁后的处理上有显著差异。

在独占模式中,线程拿到锁后仅将自己设为 Head 并切断与旧 Head 的联系。而在共享模式中,如果当前队列中还有后续节点,且允许继续共享(propagate > 0),则需要唤醒后续等待的节点一起竞争锁或自动获得许可。

setHeadAndPropagate 方法不仅执行设置 Head 的操作,还会调用 doReleaseShared 去检查并唤醒后继节点:

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;
                unparkSuccessor(h); // 唤醒后继
            }
            // ... 处理传播状态 ...
        }
        if (h == head) break;
    }
}

总结

AQS 的设计极其精巧,通过 state 统一资源状态,通过 CLH 变体队列统一管理竞争失败的线程,通过模板方法将锁的具体业务逻辑剥离给子类。掌握 acquire 的流程及其背后的自旋、CAS 和 Park/Unpark 机制,是理解 Java 并发锁体系的前提。

以上就是java同步器AQS架构AbstractQueuedSynchronizer原理解析的详细内容,更多关于java同步器AbstractQueuedSynchronizer的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表