AQS你真的真的吗?

110 阅读4分钟

AQS(AbstractQueuedSynchronizer) 是 Java 并发包(java.util.concurrent)中的一个抽象类,它是用来构建锁(Lock)和同步器(Synchronizer)框架的基础。AQS 提供了一套通用的机制,用于实现依赖于队列的并发控制结构,比如常见的锁、信号量、倒计时器等。AQS 是许多并发工具的核心,如 ReentrantLockSemaphoreCountDownLatchCyclicBarrier 等,都是基于 AQS 来实现的。

AQS 的基本原理

AQS 的设计核心是一个基于 FIFO(First-In-First-Out)等待队列的同步器。它通过一个volatile 变量 state 来表示同步状态,并使用一种模板化的方法,让子类去实现具体的同步语义。

AQS 内部维护了一个双向链表,表示多个线程在争抢资源时的等待队列。线程获取资源失败时,会进入队列进行等待,直到资源可用被唤醒。

AQS 的主要功能

AQS 提供了两种主要的锁获取模式:

  1. 独占模式(Exclusive Mode)
    在独占模式下,只有一个线程可以成功获取锁,其他线程必须等待。例如,ReentrantLock 使用的就是独占模式。
    • 如果当前线程可以获取锁(资源状态),则成功获取;
    • 如果不能,则线程会被挂起并加入等待队列,直到资源可用。
  1. 共享模式(Shared Mode)
    在共享模式下,多个线程可以同时获取资源。常见的例子是 CountDownLatch,多个线程可以共享某些资源。
    • 一次可以有多个线程成功获取资源。

AQS 的关键组件

  1. 同步状态(state)
    AQS 使用 state 变量来表示资源的状态,比如锁是否被持有,或计数器的值。state 是一个 int 类型的 volatile 变量,AQS 提供了一些原子操作来操作它(如 getState()setState()compareAndSetState())。
  2. 等待队列(Wait Queue)
    AQS 通过内部的 FIFO 队列来管理线程的等待。当一个线程无法获取资源时,它会被封装成一个 Node 对象,加入到队列中,等待资源可用时再被唤醒。
  3. 独占和共享模式
    • 在独占模式下,一个线程成功获取资源后,其他线程必须等待。
    • 在共享模式下,多个线程可以同时成功获取资源。
  1. 锁的获取和释放
    AQS 定义了 acquire()release() 方法,分别用于获取和释放资源。在获取资源失败时,线程会被阻塞并进入等待队列,而当资源被释放时,会从队列中唤醒等待的线程。

AQS 的主要方法

  • acquire(int arg) :独占模式下,线程试图获取锁。如果失败,线程进入等待队列。
  • release(int arg) :独占模式下,线程释放锁并唤醒等待队列中的线程。
  • acquireShared(int arg) :共享模式下,线程获取共享锁,成功时返回一个非负数。
  • releaseShared(int arg) :共享模式下,线程释放锁并唤醒等待线程。
  • tryAcquire(int arg) :独占模式下,尝试获取锁,需子类实现。
  • tryRelease(int arg) :独占模式下,尝试释放锁,需子类实现。
  • tryAcquireShared(int arg) :共享模式下,尝试获取共享锁,需子类实现。
  • tryReleaseShared(int arg) :共享模式下,尝试释放共享锁,需子类实现。

AQS 的使用场景

  • ReentrantLock:可重入锁,基于 AQS 实现的独占模式。
  • Semaphore:信号量,基于 AQS 实现的共享模式。
  • CountDownLatch:基于 AQS 实现的共享模式,线程等待计数器归零。
  • CyclicBarrier:基于 AQS 实现的可重复屏障机制。
  • ReentrantReadWriteLock:读写锁,其中读锁是共享模式,写锁是独占模式。

AQS 工作流程

  1. 当线程尝试获取锁时,调用 acquire() 方法,该方法内部调用 tryAcquire(),如果返回 true,表示获取成功,否则线程会被加入等待队列。
  2. 如果锁被释放,调用 release() 方法,该方法会更新 state 并唤醒队列中的等待线程。
  3. 等待的线程在合适的时候被唤醒,重新尝试获取锁。

最后总结

AQS 提供了一种通用的同步机制,使得开发人员能够方便地实现自定义的锁和同步器。通过将复杂的同步控制逻辑(如锁的竞争和管理)封装在 AQS 内部,开发者可以专注于实现高层次的业务逻辑,从而减少开发难度和提升代码质量。