跳转至

AQS 原理:AbstractQueuedSynchronizer

一、AQS 是什么

AQS(抽象队列同步器)是 JUC 锁的基础框架。ReentrantLock、CountDownLatch、Semaphore、ReentrantReadWriteLock 都基于它。

二、核心结构

1. state 状态

private volatile int state;
  • 0:无锁。
  • 0:被锁,值是重入次数。

不同实现对 state 解释不同: - ReentrantLock:持有数。 - Semaphore:剩余许可数。 - CountDownLatch:计数。

2. CLH 队列

双向链表,存等待锁的线程:

head ↔ node1 ↔ node2 ↔ node3 ↔ tail

每个 node 包一个线程,node 间通过 prev/next 连接。

3. 独占 / 共享

  • 独占:ReentrantLock,一次一个线程。
  • 共享:Semaphore、CountDownLatch,多个线程同时通过。

三、核心方法

模板方法模式

AQS 定义骨架,子类重写:

  • tryAcquire(int):尝试获取锁(独占)。
  • tryRelease(int):尝试释放锁(独占)。
  • tryAcquireShared(int):尝试获取共享。
  • tryReleaseShared(int):尝试释放共享。

骨架方法

  • acquire(int):获取锁,失败进队列。
  • release(int):释放锁,唤醒下一个。

四、acquire 流程

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  1. tryAcquire:子类实现,CAS 抢锁。
  2. 失败:addWaiter 把当前线程包成 node 加到队尾。
  3. acquireQueued:自旋检查前驱是不是 head,是就再 tryAcquire,否则挂起。

五、release 流程

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);   // 唤醒 head 的下一个节点
        return true;
    }
    return false;
}
  1. tryRelease:子类实现,state 减到 0。
  2. 唤醒 head 的后继节点。

六、为什么用 AQS

  • 队列管理、阻塞/唤醒、中断处理都封装好了。
  • 子类只需要实现 state 的获取和释放。
  • 避免每个锁都重复写队列逻辑。

高频追问

  • state 是 volatile,保证可见性。
  • CLH 队列是变体(原版不存储前驱指针)。
  • 队列不保证公平:新线程可能和队列头线程竞争。