Java 锁相关详解【六、Java AQS(AbstractQueuedSynchronizer)源码深度解析】
Java AQS(AbstractQueuedSynchronizer) 源码深度解析
一、AQS 简介
AbstractQueuedSynchronizer(简称 AQS)是 JUC 并发框架的核心基础类,几乎所有常见的并发工具(ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier、ReentrantReadWriteLock 等)都基于 AQS 构建。
它的核心思想是:
- 使用一个整数 state 表示同步状态(0/1 表示锁是否被占用,或表示剩余资源数量)。
- 通过 FIFO 队列(CLH 队列)管理等待线程。
- 提供模板方法,子类只需重写部分方法 即可实现不同的同步器。
AQS 提供了两种模式:
- 独占模式(Exclusive):同一时刻只有一个线程能持有资源(如 ReentrantLock)。
- 共享模式(Shared):多个线程可同时访问资源(如 Semaphore、CountDownLatch)。
二、核心数据结构
2.1 state
AQS 内部维护一个 volatile int state,用来表示同步状态:
- 在独占锁中,
state=0表示未加锁,1表示加锁。 - 在可重入锁中,
state记录加锁次数。 - 在共享锁中,
state可表示剩余许可数量。
更新方式:
compareAndSetState(int expect, int update)getState()/setState()
依赖 CAS 保证线程安全。
2.2 CLH 队列
AQS 使用 变种 CLH(Craig, Landin, and Hagersten)队列 来管理等待线程。
队列节点:
static final class Node {
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 当前节点代表的线程
volatile int waitStatus; // 节点状态
}
节点状态(waitStatus):
0:默认状态SIGNAL=-1:表示后继线程需要被唤醒CANCELLED=1:线程取消等待CONDITION=-2:表示节点在条件队列中PROPAGATE=-3:共享模式下的传播标识
2.3 模板方法
AQS 提供一系列需要子类实现或重写的模板方法:
-
独占模式:
tryAcquire(int arg)tryRelease(int arg)
-
共享模式:
tryAcquireShared(int arg)tryReleaseShared(int arg)
-
条件队列支持:
isHeldExclusively()
子类只需实现这些方法,线程排队/阻塞/唤醒的逻辑由 AQS 完成。
三、独占模式源码解析
以 ReentrantLock 为例,内部依赖 AQS 的独占模式。
3.1 获取锁
ReentrantLock.lock() 调用 AQS 的 acquire(int arg):
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
流程:
- 尝试直接获取锁(
tryAcquire,由子类实现)。 - 如果失败,加入等待队列(
addWaiter)。 - 在队列中自旋等待,直到前驱节点释放锁。
3.2 释放锁
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
流程:
- 调用子类实现的
tryRelease,尝试修改 state。 - 如果释放成功,唤醒后继节点。
四、共享模式源码解析
以 Semaphore 为例,内部依赖 AQS 的共享模式。
4.1 获取许可
public void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
-
tryAcquireShared返回剩余资源数:>=0:获取成功;<0:获取失败,进入队列。
4.2 释放许可
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
doReleaseShared 会唤醒后继节点,传播共享信号。
五、条件队列(Condition)
AQS 支持 ConditionObject,实现了 await/signal 机制:
await():当前线程释放锁,进入条件队列,等待被唤醒。signal():将条件队列中的一个节点转移到同步队列,准备竞争锁。
这是 synchronized + Object.wait/notify 的显式锁版本。
六、整体流程图
可以用一个 线程获取独占锁的流程图(Mermaid 表示):
七、工程实践与建议
-
尽量使用现成的并发工具类
-
不要直接继承 AQS 除非你需要实现新的同步器。
-
例如:
ReentrantLock(独占锁)Semaphore(限流器)CountDownLatch(倒计时器)ReentrantReadWriteLock(读写锁)
-
-
理解 AQS,有助于调优
ReentrantLock的公平/非公平模式本质就是 AQS 入队策略的不同。Semaphore的许可数量就是 AQS 的 state。
-
避免错误使用
- 不要直接操作 AQS 的队列或 state,必须通过模板方法。
- 子类实现需严格保证 state 的正确性。
八、总结
- AQS 是 JUC 并发工具的核心基石,通过 state + CLH 队列实现线程同步。
- 独占模式:支持互斥锁,如 ReentrantLock。
- 共享模式:支持多线程共享资源,如 Semaphore、CountDownLatch。
- ConditionObject:支持条件队列,实现等待/通知机制。
- 工程实践:优先使用 JUC 提供的工具类,理解 AQS 原理有助于调试与优化。
一句话总结:
掌握 AQS,就等于掌握了 Java 并发框架的心脏。
本文来自博客园,作者:NeoLshu,转载请注明原文链接:https://www.cnblogs.com/neolshu/p/19120846

浙公网安备 33010602011771号