Skip to content

AbstractQueuedSynchronizer (AQS) 深度解析


一、是什么?
AbstractQueuedSynchronizer(AQS)是 Java 并发包(java.util.concurrent.locks)中的核心同步框架,通过内置的 FIFO 队列管理线程阻塞/唤醒,提供了一套模板方法供开发者实现自定义同步器(如锁、信号量)。JDK 中的 ReentrantLockSemaphore 等均基于 AQS 实现。

二、解决什么问题

  1. 同步原语统一管理:避免开发者重复实现线程排队、阻塞/唤醒等底层逻辑
  2. 性能优化:通过 CAS + 自旋减少线程上下文切换
  3. 灵活性:支持独占锁(如 ReentrantLock)和共享锁(如 CountDownLatch
  4. 解决 synchronized 缺陷:提供可中断锁、超时锁、公平锁等高级特性

AQS 核心实现机制

1. 同步状态(State)

java
private volatile int state; // volatile 保证可见性
  • 整型变量,表示资源状态(如锁的重入次数、信号量的许可证数量)
  • 通过 CAS 原子操作修改:
    java
    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

2. CLH 队列(线程等待队列)

java
// 队列节点(双向链表)
static final class Node {
    volatile int waitStatus;  // 等待状态(CANCELLED/SIGNAL/CONDITION/PROPAGATE)
    volatile Thread thread;   // 绑定的线程
    volatile Node prev;
    volatile Node next;
}
private transient volatile Node head; // 队头
private transient volatile Node tail; // 队尾
  • FIFO 队列管理阻塞线程
  • 自旋检查:线程入队前短暂自旋尝试获取锁,避免直接阻塞
  • 状态标识
    • CANCELLED(1):线程已取消
    • SIGNAL(-1):后继节点需要被唤醒
    • CONDITION(-2):线程在条件队列等待

3. 模板方法(开发者需重写)

方法作用
tryAcquire(int arg)尝试获取独占锁
tryRelease(int arg)尝试释放独占锁
tryAcquireShared(int arg)尝试获取共享锁(返回剩余数量)
tryReleaseShared(int arg)尝试释放共享锁
isHeldExclusively()检查是否被当前线程独占

关键流程实现

独占锁获取(acquire())

java
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&       // 步骤1:尝试快速获取
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 步骤2-4
        selfInterrupt();
}
  1. tryAcquire():子类实现的快速获取逻辑
  2. addWaiter():将线程包装为 Node 加入队尾
  3. acquireQueued()
    • 自旋检查前驱节点是否为 head(公平锁核心)
    • 通过 LockSupport.park() 阻塞线程
  4. 被唤醒后检查中断状态

锁释放(release())

java
public final boolean release(int arg) {
    if (tryRelease(arg)) {        // 子类实现释放逻辑
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);   // 唤醒后继节点
        return true;
    }
    return false;
}
  • unparkSuccessor() 从队尾向前遍历找到最前未取消节点
  • 通过 LockSupport.unpark(node.thread) 唤醒线程

应用场景

  1. 独占锁ReentrantLock(可重入锁)
  2. 共享锁
    • Semaphore(信号量控制并发数)
    • CountDownLatch(倒计数器)
    • CyclicBarrier(循环屏障)
  3. 读写锁ReentrantReadWriteLock
  4. 自定义同步器:如数据库连接池限流

Java 示例:自定义互斥锁

java
public class MutexLock extends AbstractQueuedSynchronizer {
    
    // 尝试获取锁
    protected boolean tryAcquire(int acquires) {
        if (compareAndSetState(0, 1)) { // CAS 修改 state
            setExclusiveOwnerThread(Thread.currentThread()); // 设置独占线程
            return true;
        }
        return false;
    }
    
    // 尝试释放锁
    protected boolean tryRelease(int releases) {
        if (getState() == 0) throw new IllegalMonitorStateException();
        setExclusiveOwnerThread(null);
        setState(0); // 注意:state 修改在 owner 之后(保证可见性)
        return true;
    }
    
    // 加锁入口
    public void lock() {
        acquire(1);
    }
    
    // 解锁入口
    public void unlock() {
        release(1);
    }
}

// 使用示例
MutexLock lock = new MutexLock();
lock.lock();
try {
    // 临界区代码
} finally {
    lock.unlock();
}

重要注意事项

  1. state 设计

    • 需明确 state 语义(如 ReentrantLock 中:0=未锁定,>0=重入次数)
    • 修改 state 必须用 CAS 保证原子性
  2. 避免阻塞主线程
    tryAcquire() 中不要调用可能阻塞的操作

  3. 公平性实现

    java
    // 公平锁需检查是否有前驱节点
    protected final boolean tryAcquire(int acquires) {
        if (getState() == 0 && 
            !hasQueuedPredecessors() && // 关键公平性检查
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
        // ... 重入逻辑
    }
  4. 性能优化

    • tryAcquire() 中使用快速路径(如读锁的 ThreadLocal 计数)
    • 避免不必要的唤醒(共享锁的 PROPAGATE 状态)

AQS 与 synchronized 对比

特性AQSsynchronized
实现方式Java + CAS + 队列JVM 内置(C++实现)
锁类型支持独占/共享两种模式仅独占模式
公平性可配置公平/非公平仅非公平
锁中断支持 lockInterruptibly()不支持
超时机制支持 tryLock(timeout)不支持
条件变量支持多个 Condition仅一个 wait/notify
性能低竞争场景更优高竞争场景优化更好(JDK6+)

总结

  1. AQS 是 JUC 并发包的基石,通过模板方法模式分离通用逻辑(队列管理)与自定义逻辑(资源获取)
  2. 核心机制
    • volatile state + CLH 队列 + CAS 操作
    • 线程阻塞/唤醒通过 LockSupport 实现
  3. 最佳实践
    • 优先使用 JUC 内置同步器(如 ReentrantLock
    • 自定义同步器时明确资源访问模型(独占/共享)
    • 高并发场景测试公平/非公平锁的性能差异
  4. 演进:JDK 15 引入 VarHandle 替代 Unsafe 操作,未来版本可能进一步优化

理解 AQS 需要结合源码分析(推荐阅读 ReentrantLock 实现),掌握其"快速尝试 → 入队 → 阻塞 → 唤醒"的工作流,这是构建高性能并发工具的关键。