Java 并发基石 AQS 深度解析:从共享状态到 FIFO 等待队列的底层逻辑
·
Java 并发基石 AQS
摘要:AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发编程中核心的基础框架,位于
java.util.concurrent.locks包下。它为各种同步工具(如ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier等)提供了统一的底层实现模板,简化了同步器的设计。
AQS的核心思想
AQS的本质是通过一个共享状态(state) 和一个FIFO等待队列,实现对线程的阻塞与唤醒,从而控制多线程对共享资源的访问。
- 共享状态(state):用
volatile int类型的变量表示同步状态(如锁的持有状态、信号量的许可数量等),子类通过getState()、setState()、compareAndSetState()(CAS操作)来安全地修改状态。 - 等待队列:当线程获取资源失败时,会被封装成
Node节点加入队列尾部,进入阻塞状态;当资源被释放时,会唤醒队列中的线程重新尝试获取资源。
AQS的核心组件
-
共享状态(state)
- 是AQS的核心变量,由
volatile修饰保证可见性,通过CAS操作保证修改的原子性。 - 不同同步工具对
state的定义不同:ReentrantLock中,state表示“重入次数”(0表示未锁定,N表示被同一线程重入N次);Semaphore中,state表示“剩余许可数量”;CountDownLatch中,state表示“剩余计数”。
- 是AQS的核心变量,由
-
等待队列(CLH队列)
- 是一个双向链表,用于存储获取资源失败的线程(阻塞队列)。
- 每个节点是
Node对象,包含:- 线程引用(
Thread); - 等待状态(
waitStatus,如CANCELLED、SIGNAL、CONDITION等); - 前驱节点(
prev)和后继节点(next)。
- 线程引用(
- 队列特性:FIFO(先进先出),头节点是“已获取资源的线程”,其他节点是“等待资源的线程”。
-
核心方法
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的工作流程(以独占模式为例)
-
获取资源(acquire):
- 线程调用
acquire(arg),首先通过tryAcquire(arg)尝试获取资源(具体逻辑由子类实现,如ReentrantLock会检查state是否为0)。 - 若成功(
tryAcquire返回true),直接返回,线程继续执行。 - 若失败(
tryAcquire返回false),则将当前线程封装成Node节点,通过addWaiter(Node.EXCLUSIVE)加入等待队列尾部。 - 最后调用
acquireQueued(Node, arg),让节点在队列中自旋等待:若前驱节点是头节点,再次尝试tryAcquire;若失败则阻塞当前线程(通过LockSupport.park())。
- 线程调用
-
释放资源(release):
- 线程调用
release(arg),通过tryRelease(arg)尝试释放资源(如ReentrantLock会减少state值,直到0表示完全释放)。 - 若释放成功(
tryRelease返回true),则通过unparkSuccessor(Node)唤醒头节点的后继节点(等待队列中第一个有效线程)。 - 被唤醒的线程会再次进入自旋,尝试通过
tryAcquire获取资源,成功后成为新的头节点,继续执行。
- 线程调用
AQS的两种模式
-
独占模式(Exclusive)
- 同一时间只有一个线程能获取资源(如
ReentrantLock)。 - 节点在队列中以“独占”模式等待,唤醒时只有一个线程能竞争成功。
- 同一时间只有一个线程能获取资源(如
-
共享模式(Shared)
- 多个线程可同时获取资源(如
Semaphore、CountDownLatch)。 - 节点在队列中以“共享”模式等待,唤醒时可能有多个线程同时成功获取资源(如
Semaphore的许可数大于1时)。
- 多个线程可同时获取资源(如
AQS的优势
- 统一模板:将同步器的通用逻辑(如队列管理、线程阻塞/唤醒)封装在AQS中,子类只需关注资源的获取/释放逻辑,减少重复代码。
- 高效并发:基于CAS和FIFO队列,避免了传统锁的频繁上下文切换,性能更优。
- 灵活性:支持独占和共享两种模式,可扩展实现各种同步工具。
总结
AQS是Java并发工具的“基石”,它通过共享状态(state)和等待队列,实现了对线程的高效管理。其核心是模板方法模式:子类通过实现tryAcquire等钩子方法定义资源的获取/释放规则,而AQS则负责线程的入队、阻塞、唤醒等通用逻辑。理解AQS是掌握Java并发编程的关键,因为ReentrantLock、Semaphore等常用同步工具均基于它实现。
更多推荐
所有评论(0)