《Java并发编程研读》特别篇:AQS
·
此篇主要针对大家感兴趣的AQS详细说明,欢迎评论区指正!
整个《Java并发编程研读》专栏 如下
一、AQS究竟是什么?
AQS(AbstractQueuedSynchronizer) 不是一个具体的锁,而是一个构建锁和同步器的框架。你可以把它想象成汽车工厂的"底盘平台":同样的底盘可以造出轿车、SUV、卡车等各种车型。

二、为什么需要AQS?解决了什么问题?
1. 问题背景
在AQS出现之前,Java只有synchronized关键字实现同步,但它存在局限:
-
无法实现公平锁
-
没有超时等待机制
-
没有条件变量的精细控制
-
无法中断一个正在等待的线程
2. AQS的解决方案
AQS提供了一个通用的并发控制框架,让开发者可以:
-
自定义各种锁策略
-
实现复杂的同步需求
-
获得更好的性能
三、AQS的架构和工作原理
核心结构:CLH队列 + 状态变量

关键代码:AQS核心方法
// AQS最核心的三个部分:
public abstract class AbstractQueuedSynchronizer {
// 1. 同步状态 - 核心资源计数器
private volatile int state;
// 2. 等待队列的头尾指针
private transient volatile Node head;
private transient volatile Node tail;
// 3. 节点类 - 封装等待线程
static final class Node {
volatile Node prev;
volatile Node next;
volatile Thread thread;
volatile int waitStatus;
// ... 其他字段
}
}
AQS 的两种同步模式
独占模式(Exclusive Mode)
- 特点:同一时刻仅允许一个线程持有资源。
- 典型应用:
ReentrantLock、CountDownLatch。 - 核心方法:
acquire(int arg):获取资源(失败则入队阻塞)。release(int arg):释放资源(唤醒后继节点)。
acquire 流程

共享模式(Shared Mode)
- 特点:允许多个线程同时持有资源(如读锁、信号量)。
- 典型应用:
Semaphore、ReentrantReadWriteLock的读锁。 - 核心方法:
acquireShared(int arg):获取共享资源。releaseShared(int arg):释放共享资源(可能唤醒多个后继)。
releaseShared 的传播性
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (compareAndSetWaitStatus(h, Node.SIGNAL, 0))
unparkSuccessor(h); // 唤醒后继
}
else if (ws == 0)
compareAndSetWaitStatus(h, 0, Node.PROPAGATE);
}
if (h == head) break; // 若head未变则退出
}
}
四、实际开发中如何与AQS交互
场景1:使用现成的AQS实现
普通开发不需要直接使用AQS,而是使用基于AQS的工具类:
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
public class AQSInPractice {
public static void main(String[] args) throws InterruptedException {
// 1. ReentrantLock - 可重入锁
ReentrantLock lock = new ReentrantLock(true); // true表示公平锁
lock.lock();
try {
// 临界区代码
System.out.println("锁已获取");
} finally {
lock.unlock();
}
// 2. CountDownLatch - 线程协调
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 做一些工作
latch.countDown();
}).start();
}
latch.await(); // 等待所有线程完成
System.out.println("所有任务完成");
// 3. Semaphore - 信号量
Semaphore semaphore = new Semaphore(3); // 允许3个线程同时访问
semaphore.acquire();
try {
// 访问共享资源
} finally {
semaphore.release();
}
}
}
场景2:扩展AQS实现自定义同步器
可以继承AQS实现自定义同步需求:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
// 一个简单的"最多允许N个线程访问"的自定义锁
public class SimpleLimitLock {
private final Sync sync;
public SimpleLimitLock(int limit) {
sync = new Sync(limit);
}
// 内部Sync类继承AQS
private static class Sync extends AbstractQueuedSynchronizer {
Sync(int limit) {
setState(limit); // 初始化状态为允许的最大线程数
}
// 尝试获取锁(共享模式)
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
// 如果剩余许可不足或CAS成功,返回结果
if (remaining < 0 ||
compareAndSetState(available, remaining)) {
return remaining;
}
}
}
// 尝试释放锁(共享模式)
protected boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
if (compareAndSetState(current, next)) {
return true;
}
}
}
}
public void lock() {
sync.acquireShared(1);
}
public void unlock() {
sync.releaseShared(1);
}
}
五、AQS在Java并发体系中的角色
// Java并发编程的层次结构: ┌─────────────────────────────────────────────────┐ │ Java并发应用层 │ │ • 线程池 (ExecutorService) │ │ • 并发集合 (ConcurrentHashMap) │ │ • 异步任务 (CompletableFuture) │ ├─────────────────────────────────────────────────┤ │ AQS工具类层 │ │ • ReentrantLock │ │ • Semaphore │ │ • CountDownLatch │ │ • CyclicBarrier │ ├─────────────────────────────────────────────────┤ │ AQS框架层 │ │ • 状态管理 (state) │ │ • 队列管理 (CLH队列) │ │ • 线程阻塞/唤醒 (LockSupport) │ ├─────────────────────────────────────────────────┤ │ JVM/OS层 │ │ • synchronized监视器锁 │ │ • CAS指令 (Unsafe类) │ │ • 线程调度 (操作系统) │ └─────────────────────────────────────────────────┘
六、AQS的工作流程示例
以ReentrantLock的加锁过程为例:
// 伪代码展示AQS工作流程
class ReentrantLock {
final Sync sync;
public void lock() {
sync.acquire(1); // 调用AQS的模板方法
}
abstract static class Sync extends AbstractQueuedSynchronizer {
// AQS.acquire()的调用链:
// 1. tryAcquire() 尝试获取锁(子类实现)
// 2. addWaiter() 创建节点加入队列
// 3. acquireQueued() 在队列中自旋/阻塞
// 4. 如果被中断过,自我中断恢复状态
}
}
具体过程可以用图表示:

七、实际开发中的最佳实践
1. 选择合适的同步工具
// 不同场景使用不同的AQS工具
public class SyncToolSelection {
// 场景1:需要可重入、公平性的互斥锁
void useReentrantLock() {
ReentrantLock lock = new ReentrantLock(true); // 公平锁
// 适用于资源竞争激烈的场景
}
// 场景2:控制并发线程数
void useSemaphore() {
Semaphore semaphore = new Semaphore(10); // 最多10个线程同时访问
// 适用于数据库连接池、限流场景
}
// 场景3:等待多个任务完成
void useCountDownLatch() {
CountDownLatch latch = new CountDownLatch(5);
// 适用于启动阶段等待初始化完成
}
// 场景4:循环等待
void useCyclicBarrier() {
CyclicBarrier barrier = new CyclicBarrier(4);
// 适用于并行计算,等待所有线程到达屏障
}
}
2. 避免常见陷阱
public class AQSPitfalls {
// 陷阱1:忘记释放锁
void wrongWay() {
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 业务代码
if (someCondition) {
return; // 这里直接返回,忘记解锁!
}
// 更多代码
} finally {
lock.unlock(); // 必须在finally中释放
}
}
// 陷阱2:错误使用条件变量
void conditionVariableMistake() {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// 错误:没有在lock保护下调用await
// condition.await();
// 正确:
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
}
}
八、总结:AQS在并发编程中的角色
-
框架提供者:为锁和同步器提供通用实现框架
-
基础设施:是Java并发包的基石,如大楼的地基
-
性能优化:相比synchronized提供更灵活的优化空间
-
功能扩展:支持公平/非公平、可重入、读写分离等高级特性
更多推荐



所有评论(0)