AQS(AbstractQueuedSynchronizer)是Java并发包的基础框架,它封装了实现同步器(如ReentrantLock、Semaphore等)所需的核心逻辑,让开发者能轻松构建自定义的并发工具。

核心设计思路

  1. 资源状态管理
    用一个 volatile int state 表示共享资源的状态:

    • state = 0 表示资源空闲;
    • 可根据需要表示不同含义(如锁的重入次数、信号量的剩余许可证数)。
      使用 volatile 保证多线程间的可见性,并通过 CAS(Compare-And-Swap) 原子地修改 state,避免加锁开销。
  2. 等待队列
    当CAS修改state失败(即资源被占用)时,线程不能一直自旋浪费CPU,而是需要被阻塞。AQS 用 双向链表 实现了一个 FIFO 等待队列(CLH锁的变体),将获取资源失败的线程封装成 Node 节点入队,并通过 LockSupport.park/unpark 实现线程的阻塞与唤醒。队列结构也为实现 公平策略 提供了基础(先入队的线程优先获得资源)。

  3. 模板方法模式
    AQS 定义了资源获取与释放的骨架,但将具体的获取逻辑(如是否允许重入、是否允许插队)留给子类实现。典型方法如:

    • tryAcquire():尝试获取资源(返回 boolean),由子类重写;
    • acquire():若 tryAcquire() 失败,则将当前线程入队并阻塞,直到被唤醒后再次尝试。
      这样既复用了排队、阻塞等通用逻辑,又保留了灵活性。
  4. 独占模式与共享模式

    • 独占模式(如 ReentrantLock):同一时刻只允许一个线程访问资源,state 通常表示锁的持有次数(可重入)。
    • 共享模式(如 Semaphore、CountDownLatch):允许多个线程同时访问,state 表示剩余许可证数或需等待的任务数。
  5. 条件队列(Condition)
    AQS 通过内部 ConditionObject 实现了类似 Object.wait/notify 的线程等待/唤醒机制。每个 Condition 维护一个独立的条件队列,调用 await() 的线程会释放资源并进入条件队列等待;signal() 则将条件队列中的节点移到等待队列,等待重新竞争资源。支持创建多个 Condition,实现更精细化的线程协调。

为什么不用操作系统 Mutex?

  • 性能:直接使用内核态的 mutex 会频繁触发用户态/内核态切换,开销大。AQS 基于用户态的 CAS 和队列管理,在竞争不激烈时避免了系统调用。
  • 灵活性:AQS 在 Java 层面提供了可扩展的模板,允许开发者自定义公平策略、共享/独占模式等,而操作系统原语无法直接实现这种灵活性。

与 synchronized 的对比

  • 可中断与超时:ReentrantLock 基于 AQS 的 tryAcquire 可提供 tryLock 超时、中断响应等能力,而 synchronized 不支持。
  • 公平性:AQS 的队列结构让 ReentrantLock 能轻松实现公平锁(先来后到),synchronized 只能是非公平锁。
  • 条件队列:ReentrantLock 可创建多个 Condition 实现精准唤醒,synchronized 仅支持单一的 wait/notify 集。

简言之,AQS 通过 状态变量(state)+ CAS + 等待队列 + 模板方法 构建了一个高效、灵活的多线程同步框架,成为 Java 并发工具(如 ReentrantLock、Semaphore、CountDownLatch 等)的基石。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐