【Java】AQS (AbstractQueuedSynchronizer)

7 阅读3分钟

AQS 是 Java 并发包 (java.util.concurrent.locks) 的核心基础框架,它为实现各种同步器提供了可重用的基础设施。

1. AQS 核心设计思想

AQS 采用 模板方法模式,将同步器的实现分为两部分:

  • 共享资源状态管理:通过一个 volatile int 类型的 state 表示
  • 线程排队机制:采用 CLH 变体的 FIFO 双向队列

1.1 核心数据结构

// 同步队列节点
static final class Node {
    volatile int waitStatus;      // 等待状态
    volatile Node prev;           // 前驱节点
    volatile Node next;           // 后继节点
    volatile Thread thread;       // 关联线程
    Node nextWaiter;              // 条件队列链接
}

// 关键字段
private transient volatile Node head;   // 队首
private transient volatile Node tail;   // 队尾
private volatile int state;             // 同步状态

2. 同步状态管理

2.1 状态访问方法

// 获取状态
protected final int getState() {
    return state;
}

// 设置状态
protected final void setState(int newState) {
    state = newState;
}

// CAS 更新状态
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2.2 状态语义

  • state 的具体语义由子类定义,例如:

    • ReentrantLock:表示持有锁的计数
    • Semaphore:表示可用许可数
    • CountDownLatch:表示剩余计数

3. 同步队列工作原理

3.1 入队流程(获取资源失败时)

  1. 创建节点并入队(CAS 设置 tail)
  2. 自旋检查前驱节点状态
  3. 必要时挂起线程(通过 LockSupport.park()
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; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

3.2 出队流程(释放资源时)

  1. 更新 state 状态
  2. 唤醒后继节点(通过 LockSupport.unpark()
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

4. 关键模板方法

AQS 定义以下需要子类实现的方法:

方法名作用
tryAcquire(int)尝试获取独占锁
tryRelease(int)尝试释放独占锁
tryAcquireShared(int)尝试获取共享锁
tryReleaseShared(int)尝试释放共享锁
isHeldExclusively()判断是否被当前线程独占

5. 两种同步模式

5.1 独占模式 (Exclusive)

  • 同一时刻只有一个线程能获取资源
  • 实现类:ReentrantLock
// 获取锁示例
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

5.2 共享模式 (Shared)

  • 多个线程可以同时获取资源
  • 实现类:SemaphoreCountDownLatch
// 获取共享锁示例
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

6. 条件变量支持

AQS 通过 ConditionObject 实现条件等待机制:

public class ConditionObject implements Condition {
    private transient Node firstWaiter;   // 条件队列头
    private transient Node lastWaiter;    // 条件队列尾
    
    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        Node node = addConditionWaiter();
        int savedState = fullyRelease(node);
        // ...
    }
    
    public final void signal() {
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        Node first = firstWaiter;
        if (first != null)
            doSignal(first);
    }
}

7. AQS 在 JUC 中的应用

同步器AQS 使用方式
ReentrantLock独占模式,state 表示重入次数
ReentrantReadWriteLock高16位表示读锁,低16位表示写锁
Semaphore共享模式,state 表示可用许可数
CountDownLatch共享模式,state 表示剩余计数
FutureTask表示任务状态

8. 关键设计优势

  1. 性能优化

    • 自旋尝试获取锁减少线程挂起
    • 通过 CAS 避免锁竞争
    • 延迟初始化队列头节点
  2. 灵活性

    • 支持公平/非公平策略
    • 同时支持独占和共享模式
    • 可扩展条件变量
  3. 可靠性

    • 正确处理线程中断
    • 避免死锁设计
    • 完善的取消机制