Java 并发基石 AQS

摘要:AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发编程中核心的基础框架,位于java.util.concurrent.locks包下。它为各种同步工具(如ReentrantLockSemaphoreCountDownLatchCyclicBarrier等)提供了统一的底层实现模板,简化了同步器的设计。

AQS的核心思想

AQS的本质是通过一个共享状态(state)一个FIFO等待队列,实现对线程的阻塞与唤醒,从而控制多线程对共享资源的访问。

  • 共享状态(state):用volatile int类型的变量表示同步状态(如锁的持有状态、信号量的许可数量等),子类通过getState()setState()compareAndSetState()(CAS操作)来安全地修改状态。
  • 等待队列:当线程获取资源失败时,会被封装成Node节点加入队列尾部,进入阻塞状态;当资源被释放时,会唤醒队列中的线程重新尝试获取资源。

AQS的核心组件

  1. 共享状态(state)

    • 是AQS的核心变量,由volatile修饰保证可见性,通过CAS操作保证修改的原子性。
    • 不同同步工具对state的定义不同:
      • ReentrantLock中,state表示“重入次数”(0表示未锁定,N表示被同一线程重入N次);
      • Semaphore中,state表示“剩余许可数量”;
      • CountDownLatch中,state表示“剩余计数”。
  2. 等待队列(CLH队列)

    • 是一个双向链表,用于存储获取资源失败的线程(阻塞队列)。
    • 每个节点是Node对象,包含:
      • 线程引用(Thread);
      • 等待状态(waitStatus,如CANCELLEDSIGNALCONDITION等);
      • 前驱节点(prev)和后继节点(next)。
    • 队列特性:FIFO(先进先出),头节点是“已获取资源的线程”,其他节点是“等待资源的线程”。
  3. 核心方法
    AQS通过模板方法模式定义了一套通用逻辑,子类只需实现特定抽象方法(钩子方法)即可。核心方法分为两类:

    • 资源获取

      • 独占模式:acquire(int arg)(获取资源,失败则入队阻塞);
      • 共享模式:acquireShared(int arg)(多个线程可同时获取资源)。
    • 资源释放

      • 独占模式:release(int arg)(释放资源,成功则唤醒队列线程);
      • 共享模式:releaseShared(int arg)

    子类需实现的钩子方法(根据模式选择):

    • 独占模式:tryAcquire(int arg)(尝试获取资源)、tryRelease(int arg)(尝试释放资源);
    • 共享模式:tryAcquireShared(int arg)tryReleaseShared(int arg)

AQS的工作流程(以独占模式为例)

  1. 获取资源(acquire)

    • 线程调用acquire(arg),首先通过tryAcquire(arg)尝试获取资源(具体逻辑由子类实现,如ReentrantLock会检查state是否为0)。
    • 若成功(tryAcquire返回true),直接返回,线程继续执行。
    • 若失败(tryAcquire返回false),则将当前线程封装成Node节点,通过addWaiter(Node.EXCLUSIVE)加入等待队列尾部。
    • 最后调用acquireQueued(Node, arg),让节点在队列中自旋等待:若前驱节点是头节点,再次尝试tryAcquire;若失败则阻塞当前线程(通过LockSupport.park())。
  2. 释放资源(release)

    • 线程调用release(arg),通过tryRelease(arg)尝试释放资源(如ReentrantLock会减少state值,直到0表示完全释放)。
    • 若释放成功(tryRelease返回true),则通过unparkSuccessor(Node)唤醒头节点的后继节点(等待队列中第一个有效线程)。
    • 被唤醒的线程会再次进入自旋,尝试通过tryAcquire获取资源,成功后成为新的头节点,继续执行。

AQS的两种模式

  1. 独占模式(Exclusive)

    • 同一时间只有一个线程能获取资源(如ReentrantLock)。
    • 节点在队列中以“独占”模式等待,唤醒时只有一个线程能竞争成功。
  2. 共享模式(Shared)

    • 多个线程可同时获取资源(如SemaphoreCountDownLatch)。
    • 节点在队列中以“共享”模式等待,唤醒时可能有多个线程同时成功获取资源(如Semaphore的许可数大于1时)。

AQS的优势

  • 统一模板:将同步器的通用逻辑(如队列管理、线程阻塞/唤醒)封装在AQS中,子类只需关注资源的获取/释放逻辑,减少重复代码。
  • 高效并发:基于CAS和FIFO队列,避免了传统锁的频繁上下文切换,性能更优。
  • 灵活性:支持独占和共享两种模式,可扩展实现各种同步工具。

总结

AQS是Java并发工具的“基石”,它通过共享状态(state)和等待队列,实现了对线程的高效管理。其核心是模板方法模式:子类通过实现tryAcquire等钩子方法定义资源的获取/释放规则,而AQS则负责线程的入队、阻塞、唤醒等通用逻辑。理解AQS是掌握Java并发编程的关键,因为ReentrantLockSemaphore等常用同步工具均基于它实现。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐