JUC之AQS
-
-
- 核心思想:资源共享与同步状态
-
- [1. 同步状态 (state)](#1. 同步状态 (state))
- [2. CLH 等待队列](#2. CLH 等待队列)
- 两大资源共享模式
- 关键设计:模板方法模式
- [一个简化的例子:理解如何使用 AQS](#一个简化的例子:理解如何使用 AQS)
- [总结:AQS 的价值](#总结:AQS 的价值)
-
AQS 是 AbstractQueuedSynchronizer 的缩写,它是 java.util.concurrent(JUC)包中一个至关重要的 抽象类 ,可以理解为实现同步器(锁、信号量等)的"万能框架"。
你可以把它看作是 Java 并发包中众多同步工具背后的底层基础设施 。ReentrantLock、CountDownLatch、Semaphore 等我们常用的并发工具,都是基于 AQS 构建的。
核心思想:资源共享与同步状态
AQS 的核心是解决了两个关键问题:
- 资源如何共享? ------ 同步状态(state)
- 没有抢到资源的线程怎么办? ------ 等待队列
1. 同步状态 (state)
AQS 使用一个 volatile int state 来表示共享资源。你可以根据需求赋予它不同的含义:
- 在
ReentrantLock中,state = 0表示锁未被占用,state > 0表示被占用,且数值可以表示重入次数。 - 在
Semaphore中,state表示剩余的许可证数量。 - 在
CountDownLatch中,state表示还需要等待的计数。
通过 getState(), setState(), compareAndSetState() 这三个原子操作方法来修改状态。
2. CLH 等待队列
当线程获取同步状态失败时,AQS 会将其封装成一个 Node 节点,并放入一个 先进先出(FIFO)的双向队列 中。这个队列是 AQS 实现等待/通知机制的关键。
队列中的线程会被阻塞(LockSupport.park()),当同步状态释放时,AQS 会从队列头部唤醒等待的线程(unpark())。
两大资源共享模式
AQS 为子类提供了两种主要的工作模式:
| 模式 | 说明 | 典型代表 |
|---|---|---|
| 独占模式 | 只有一个线程能持有同步状态。 | ReentrantLock |
| 共享模式 | 多个线程可以同时持有同步状态。 | CountDownLatch, Semaphore, ReadWriteLock 的读锁 |
AQS 中的队列同时支持这两种模式,Node 节点会标记自己是 SHARED 还是 EXCLUSIVE。
关键设计:模板方法模式
AQS 是一个抽象类,它定义好了"骨架逻辑"(比如获取资源失败后如何排队、阻塞),但把"如何判断资源是否可用"这个具体逻辑,留给子类去实现。
你只需要重写 AQS 提供的几个特定方法,AQS 的 acquire、release 等模板方法就会自动调用它们:
tryAcquire(arg):独占式尝试获取状态(需子类实现)tryRelease(arg):独占式尝试释放状态(需子类实现)tryAcquireShared(arg):共享式尝试获取状态(需子类实现)tryReleaseShared(arg):共享式尝试释放状态(需子类实现)isHeldExclusively():判断当前线程是否独占资源
一个简化的例子:理解如何使用 AQS
假设我们想实现一个简单的互斥锁(一次只允许一个线程进入),可以这样利用 AQS:
java
// 1. 定义一个同步器类,继承 AQS
class MutexSync extends AbstractQueuedSynchronizer {
// 尝试获取锁:将 state 从 0 改为 1
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, 1);
}
// 尝试释放锁:将 state 从 1 改为 0
@Override
protected boolean tryRelease(int arg) {
setState(0);
return true;
}
// 当前是否处于占用状态
@Override
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
// 2. 定义锁的公开接口,使用 AQS 提供的模板方法
public class SimpleLock {
private final MutexSync sync = new MutexSync();
public void lock() {
// 调用 AQS 的模板方法,它会调用我们重写的 tryAcquire
sync.acquire(1);
}
public void unlock() {
// 调用 AQS 的模板方法,它会调用我们重写的 tryRelease
sync.release(1);
}
}
你不需要关心排队、阻塞、唤醒等复杂的底层逻辑,AQS 都为你处理好了。
总结:AQS 的价值
- 对开发者 :提供了构建锁和同步器的标准化模板,复用了高质量的并发控制代码,避免重复造轮子。
- 对 JUC 框架 :是整个
java.util.concurrent包的基石 ,支撑起了ReentrantLock、CountDownLatch、Semaphore等众多高级工具。 - 对学习并发 :理解 AQS 的
state状态和CLH 队列,是深入理解 Java 并发机制的关键一步。