juc-aqs
# 1 AQS AbstractQueuedSynchronizer 队列同步器
AQS(AbstractQueuedSynchronizer)是Java中的一个重要的同步框架,它提供了一种基础框架来实现各种类型的同步器,如锁、信号量、倒计时门闩等。AQS是Java并发包(java.util.concurrent)的核心组件之一,它用于支持构建自定义的同步工具和数据结构。
AQS的核心思想是使用一个FIFO(先进先出)的等待队列来管理线程的竞争和等待。
AQS内部维护了一个状态变量,线程在尝试获取锁或执行某个操作时,首先会尝试修改这个状态,如果修改成功(比如从0修改为1),则表示线程获得了锁或操作权限;如果修改失败,则线程会被加入到等待队列中,等待状态变为满足某个条件时再次尝试获取锁或操作权限。
AQS的两个主要子类是ReentrantLock
(可重入锁)和CountDownLatch
(倒计时门闩)。ReentrantLock
是一个可重入的互斥锁,而CountDownLatch
是一个同步工具,它允许一个或多个线程等待其他线程完成一组操作。
自定义同步器时,通常需要继承AbstractQueuedSynchronizer
并实现以下两个主要方法:
tryAcquire
:尝试获取锁或执行某个操作的逻辑。如果返回true,表示操作成功;如果返回false,表示操作失败,线程会被加入到等待队列中。tryRelease
:尝试释放锁或完成某个操作的逻辑。这个方法通常用于释放资源并唤醒等待队列中的线程。
AQS的设计使得实现各种自定义的同步器变得相对容易,开发者可以根据具体的需求来编写自己的tryAcquire
和tryRelease
方法,从而构建出高性能的同步工具。
总之,AQS(AbstractQueuedSynchronizer)是Java并发包中的一个核心组件,用于支持构建自定义的同步工具和数据结构,它通过等待队列和状态变量的方式实现了多线程的协作和同步。
# 2 优势
AQS 解决了在实现同步器时涉及当的大量细节问题,例如获取同步状态、FIFO 同步队列。基于 AQS 来构建同步器可以带来很多好处。它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。
在基于 AQS 构建的同步器中,只能在一个时刻发生阻塞,从而降低上下文切换的开销,提高了吞吐量。同时在设计 AQS 时充分考虑了可伸缩性,因此 J.U.C 中,所有基于 AQS 构建的同步器均可以获得这个优势。
# 3 同步状态
AQS 的主要使用方式是继承,子类通过继承同步器,并实现它的抽象方法来管理同步状态。
AQS 使用一个 int
类型的成员变量 state
来表示同步状态:
- 当
state > 0
时,表示已经获取了锁。 - 当
state = 0
时,表示释放了锁。
它提供了三个方法,来对同步状态 state
进行操作,并且 AQS 可以确保对 state
的操作是安全的:
#getState()
#setState(int newState)
#compareAndSetState(int expect, int update)
# 4 同步队列
AQS 通过内置的 FIFO 同步队列来完成资源获取线程的排队工作:
- 如果当前线程获取同步状态失败(锁)时,AQS 则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程
- 当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。
# 5. 主要内置方法
AQS 主要提供了如下方法:
#getState()
:返回同步状态的当前值。#setState(int newState)
:设置当前同步状态。#compareAndSetState(int expect, int update)
:使用 CAS 设置当前状态,该方法能够保证状态设置的原子性。- 【可重写】
#tryAcquire(int arg)
:独占式获取同步状态,获取同步状态成功后,其他线程需要等待该线程释放同步状态才能获取同步状态。 - 【可重写】
#tryRelease(int arg)
:独占式释放同步状态。 - 【可重写】
#tryAcquireShared(int arg)
:共享式获取同步状态,返回值大于等于 0 ,则表示获取成功;否则,获取失败。 - 【可重写】
#tryReleaseShared(int arg)
:共享式释放同步状态。 - 【可重写】
#isHeldExclusively()
:当前同步器是否在独占式模式下被线程占用,一般该方法表示是否被当前线程所独占。 acquire(int arg)
:独占式获取同步状态。如果当前线程获取同步状态成功,则由该方法返回;否则,将会进入同步队列等待。该方法将会调用可重写的#tryAcquire(int arg)
方法;#acquireInterruptibly(int arg)
:与#acquire(int arg)
相同,但是该方法响应中断。当前线程为获取到同步状态而进入到同步队列中,如果当前线程被中断,则该方法会抛出InterruptedException 异常并返回。#tryAcquireNanos(int arg, long nanos)
:超时获取同步状态。如果当前线程在 nanos 时间内没有获取到同步状态,那么将会返回 false ,已经获取则返回 true 。#acquireShared(int arg)
:共享式获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态;#acquireSharedInterruptibly(int arg)
:共享式获取同步状态,响应中断。#tryAcquireSharedNanos(int arg, long nanosTimeout)
:共享式获取同步状态,增加超时限制。#release(int arg)
:独占式释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒。#releaseShared(int arg)
:共享式释放同步状态。
从上面的方法看下来,基本上可以分成 3 类:
- 独占式获取与释放同步状态
- 共享式获取与释放同步状态
- 查询同步队列中的等待线程情况