AQS 是用来为Java的并发同步组件提供统一的底层支持

AQS有2个重要组成部分:

  1. state 同步状态,int 类型
  2. 一个同步队列

在基于AQS构建的同步器类(如 ReentrantLock,CountDownLatch)中,最基本的操作包括各种形式的获取操作释放操作。获取操作是一种依赖状态的操作,并且通常会阻塞。释放操作并可非阻塞的操作,当执行释放操作时,所有在请求时被阻塞的线程都会开始执行。

如果一个类想成为状态依赖的类,那么它必须拥有一些状态。AQS负责管理同步器类中的状态,它管理了一个整数状态信息,可以通过 getStatesetStatecompareAndSetState 等protected 类型方法来进行操作。

这个整数可以用于表示任意状态。例如 ReentrantLock 用它来表示所有者线程已经重复获取该锁的次数,Semaphore 用它来表示剩余的许可数量,FutureTask 用它来表示任务的状态(尚未开始、正在运行、已完成以及已取消)。在同步器类中还可以自行管理一些额外的状态变量,例如,ReentrantLock 保存了锁的当前所有者的信息,这样就能区分某个获取操作是冲入的还是竞争的。

根据同步器的不同,AQS的获取操作可以是一种独占操作(例如ReentrantLock),也可以是一个非独占操作(例如SemaphoreCountDownLatch)。一个获取操作包括两部分。

  1. 同步器判断当前状态是否允许获得操作,如果是,则允许线程执行,否则获取操作将阻塞或失败。这种判断是由同步器的语义决定的。例如,对于锁来说,如果它没有被某个线程持有,那么就能被成功地获取,而对于闭锁(Latch)来说,如果它是处于结束状态,那么也能被成功获取
  2. 其次,就是更新同步器地状态,获取同步器地某个线程可能会对其他线程能否也获取该同步器造成影响。例如,当获取一个锁后,锁地状态将从 “未被持有” 编程 “已被持有”,而从 Semaphore 中获取一个许可后,将把剩余许可地数量减 1。然而,当一个线程获取闭锁时,并不会影响其它线程能否获取它,因此获取闭锁地操作不会改变闭锁地状态。

如果某个同步器支持独占的操作,那么需要实现一些保护方法,包括:tryAcquiretryReleaseisHeldExclusively 等。

对于支持共享获取的同步器,则应该实现 tryAcquireSharedtryReleaseShared 等方法。

AQS 中的 acquireacquireSharedreleasereleaseShared 等方法都将调用这些方法在子类中带有前缀 try 的版本来判断某个操作是否能执行。在同步器的子类中,可以根据其获取操作和释放操作的语义,使用 setStategetStatecompareAndSetState 来检查和更新状态,并通过返回的状态值来告知基类 “获取” 或 “释放” 同步器的操作是否成功。例如:如果 tryAcquireShared 返回一个负值示获取操作失败,返回零值表示同步器通过独占方式被获取,返回正值则表示同步器通过独占方式获取。对于 tryReleasetryReleaseShared 方法来说,如果释放操作使得所有在获取同步器时被阻塞的线程恢复执行,那么这两个方法应该返回 true。

AQS内部维护了一个基于 CLH 变种的 阻塞队列(双向链表)(FIFO),资源空闲时,头节点可以直接获取锁,后继节点通过自选方式判断前置节点是否完成。CLH 是一个有序的无饥饿的公平锁,比较经典的实现是 ReentrantLock 的公平锁。