一、AQS 是什么?
1.1 定义
AbstractQueuedSynchronizer(AQS)是 Java 并发包(java.util.concurrent.locks)中的抽象同步器框架 ,由 Doug Lea 设计,用于构建锁 和 同步器。
java
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer {
// 三大核心组件
private volatile int state; // 同步状态
private transient volatile Node head; // 等待队列头
private transient volatile Node tail; // 等待队列尾
}
1.2 目标
将**"资源状态管理"与"线程排队/唤醒机制"**解耦:
- 子类只需定义何时允许线程获取资源(通过重写钩子方法);
- AQS 负责线程阻塞、入队、唤醒、中断处理等复杂逻辑。
1.3 AQS 在 JUC 中的位置
graph TD
A[AQS \n AbstractQueuedSynchronizer] --> B[ReentrantLock]
A --> C[CountDownLatch]
A --> D[Semaphore]
A --> E[CyclicBarrier]
A --> F[ReentrantReadWriteLock]
B --> B1[公平锁/非公平锁]
C --> C1[计数器同步]
D --> D1[信号量控制]
E --> E1[循环屏障]
F --> F1[读写分离]
注意:CyclicBarrier不是基于 AQS 实现,而是基于 ReentrantLock + Condition。
二、AQS 核心设计思想
2.1 模板方法模式
AQS 是模板方法模式的经典应用,将同步器的实现分为:
| 类型 | 方法 | 说明 |
|---|---|---|
| 模板方法 | acquire(), release(), acquireShared() 等 | 线程排队、阻塞、唤醒等通用逻辑,不可重写 |
| 钩子方法 | tryAcquire(), tryRelease(), tryAcquireShared() 等 | 资源获取/释放的具体语义,必须由子类实现 |
java
// 模板方法(AQS实现固定流程)
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 钩子方法:子类实现
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 钩子方法(子类实现具体语义)
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException(); // 必须重写!
}
- AQS负责:线程队列管理、阻塞唤醒机制
- 子类负责:资源状态管理、获取释放逻辑
2.2 两种同步模式
| 特性 | 独占模式 | 共享模式 |
|---|---|---|
| 同时获取线程数 | 1个 | 多个 |
| 节点模式 | Node.EXCLUSIVE | Node.SHARED |
| tryAcquire返回值 | boolean | int |
| 唤醒策略 | 精确唤醒一个后继 | 传播唤醒多个后继 |
| 典型应用 | ReentrantLock | CountDownLatch, Semaphore |
| 资源语义 | 排他性资源 | 可共享资源 |
关键区别 :共享模式支持一个释放可能唤醒多个线程。
三、AQS 核心数据结构
3.1 同步状态
state字段是AQS的核心状态标识,不同同步器赋予其不同语义:
java
public abstract class AbstractQueuedSynchronizer {
private volatile int state; // volatile保证可见性
// 状态访问方法
protected final int getState() {
return state;
}
//状态设置方法
protected final void setState(int newState) {
state = newState;
}
//通过CAS原子更新
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
不同同步器的state语义:
| 同步器 | state含义 | 数值范围 |
|---|---|---|
| ReentrantLock | 重入次数 | 0 = 未锁定, ≥1 = 锁定次数 |
| CountDownLatch | 剩余计数 | N → 0 |
| Semaphore | 可用许可数 | ≥ 0 |
| ReentrantReadWriteLock | 读写状态 | 高16位 = 读锁, 低16位 = 写锁 |
3.2 CLH队列节点
AQS 使用双向链表实现的 CLH 队列变体管理等待线程。,队列节点结构如下:
java
static final class Node {
// 节点模式
static final Node SHARED = new Node(); // 共享模式
static final Node EXCLUSIVE = null; // 独占模式
// 等待状态
static final int CANCELLED = 1; // 线程已取消(超时/中断)
static final int SIGNAL = -1; // 后继节点需要被唤醒
static final int CONDITION = -2; // 在条件队列中等待
static final int PROPAGATE = -3; // 共享模式下传播唤醒
// 节点属性
volatile int waitStatus; // 等待状态
volatile Node prev; // 前驱节点
volatile Node next; // 后继节点
volatile Thread thread; // 等待线程
Node nextWaiter; // 下一个等待者(条件队列)
}
3.3 等待队列可视化
graph TB
%% ========== 样式设置 ==========
classDef headNode fill:#e1f5fe,stroke:#01579b,stroke-width:2px
classDef signalNode fill:#c8e6c9,stroke:#2e7d32,stroke-width:2px
classDef cancelledNode fill:#ffcdd2,stroke:#c62828,stroke-width:2px
classDef tailNode fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
classDef threadNode fill:#fff3e0,stroke:#ef6c00,stroke-width:2px
classDef stateNode fill:#e8f5e8,stroke:#388e3c,stroke-width:1px
class H,T headNode,tailNode
class A,B signalNode
class C cancelledNode
class T1,T2,T3 threadNode
class S1,S2,S3,S1_1,S2_1,S3_1 stateNode
%% ========== 队列操作说明 ==========
subgraph 队列操作流程
O1[1. 新线程入队] --> O2[创建Node节点并添加到队列尾部]
O2 --> O3[设置前驱节点状态为SIGNAL]
O3 --> O4[线程进入等待状态]
O5[2. 节点出队] --> O6[头节点后继获取资源成功]
O6 --> O7[该节点成为新头节点]
O7 --> O8[原头节点断开连接]
end
graph TB
%% ========== 主队列结构 ==========
subgraph AQS等待队列
direction LR
H[虚拟头节点
Head] A[节点A
SIGNAL状态] B[节点B
SIGNAL状态] C[节点C
CANCELLED状态] T[队列尾部
Tail] H -->|next| A A -->|prev| H A -->|next| B B -->|prev| A B -->|next| C C -->|prev| B C -->|next| T T -->|prev| C end %% ========== 线程关联 ========== subgraph 等待线程 T1[Thread-1
运行状态: WAITING] T2[Thread-2
运行状态: WAITING] T3[Thread-3
运行状态: CANCELLED] T1 -->|关联| A T2 -->|关联| B T3 -->|关联| C end
Head] A[节点A
SIGNAL状态] B[节点B
SIGNAL状态] C[节点C
CANCELLED状态] T[队列尾部
Tail] H -->|next| A A -->|prev| H A -->|next| B B -->|prev| A B -->|next| C C -->|prev| B C -->|next| T T -->|prev| C end %% ========== 线程关联 ========== subgraph 等待线程 T1[Thread-1
运行状态: WAITING] T2[Thread-2
运行状态: WAITING] T3[Thread-3
运行状态: CANCELLED] T1 -->|关联| A T2 -->|关联| B T3 -->|关联| C end
状态说明:
| 状态类型 | 数值 | 含义 | 线程行为 |
|---|---|---|---|
| SIGNAL | -1 | 需要唤醒后继节点 | 线程等待中,需要前驱唤醒 |
| CANCELLED | 1 | 线程已取消获取 | 线程已中断或超时 |
| CONDITION | -2 | 在条件队列等待 | 线程在Condition队列 |
| PROPAGATE | -3 | 共享模式传播 | 共享模式下唤醒传播 |
队列特点:
- FIFO双向链表:支持高效的节点插入和删除
- 虚拟头节点:简化边界条件处理
- 状态驱动:通过节点状态控制线程阻塞和唤醒
3.4 条件队列
java
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
// 条件等待:释放锁,加入条件队列,被唤醒后重新竞争锁
public final void await() throws InterruptedException {
if (Thread.interrupted()) throw new InterruptedException();
Node node = addConditionWaiter(); // 加入条件队列
int savedState = fullyRelease(node); // 完全释放锁
// ... 等待被signal唤醒
}
}
四、AQS 核心工作流程
4.1 独占模式:获取资源(acquire)
java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
执行流程
flowchart TD
A[调用 acquire] --> B{tryAcquire 成功?}
B -- 是 --> C[直接返回]
B -- 否 --> D[addWaiter: 创建节点入队]
D --> E[acquireQueued: 自旋等待]
E --> F{前驱是 head 且 tryAcquire 成功?}
F -- 是 --> G[setHead, 返回]
F -- 否 --> H{shouldPark?}
H -- 是 --> I[park 阻塞]
I --> J[被唤醒后继续循环]
H -- 否 --> E
4.1.1 步骤1:尝试快速获取(tryAcquire)
java
// 由子类实现,如ReentrantLock中的实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
4.1.2 步骤2:创建节点并入队(addWaiter)
java
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 快速路径:直接CAS设置尾节点
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) { // CAS原子操作
pred.next = node;
return node;
}
}
// 慢速路径:CAS失败或队列未初始化
enq(node);
return node;
}
// 自旋入队,保证节点一定成功加入队列
private Node enq(final Node node) {
for (;;) { // 自旋直到成功
Node t = tail;
if (t == null) { // 队列为空,需要初始化
if (compareAndSetHead(new Node())) // 设置虚拟头节点
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) { // CAS设置尾节点
t.next = node;
return t;
}
}
}
}
4.1.3 步骤3:队列中自旋获取(acquireQueued)
java
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 自旋检查
final Node p = node.predecessor(); // 获取前驱节点
// 关键条件:只有前驱是头节点才有资格尝试获取锁
if (p == head && tryAcquire(arg)) {
setHead(node); // 获取成功,设置为新头节点
p.next = null; // 帮助GC
failed = false;
return interrupted;
}
// 检查是否需要挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 挂起并检查中断
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node); // 取消获取(异常或中断)
}
}
4.1.4 步骤4:判断是否需要挂起(shouldParkAfterFailedAcquire)
java
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL) // 前驱节点承诺唤醒我
return true;
if (ws > 0) { // 前驱节点已取消,需要跳过这些节点
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
// 设置前驱节点状态为SIGNAL,表示需要它唤醒我
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false; // 第一次返回false,给一次重试机会
}
// 挂起线程并检查中断状态
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 挂起当前线程
return Thread.interrupted(); // 返回并清除中断状态
}
4.2 独占模式释放锁(release)
java
public final boolean release(int arg) {
if (tryRelease(arg)) { // 子类实现释放逻辑
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
// 唤醒后继节点
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0); // 清除状态
Node s = node.next;
// 如果后继节点无效,从尾部向前查找有效节点
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 唤醒线程
}
4.3 共享模式核心方法
4.3.1 共享获取(acquireShared)
java
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) // 返回值语义:
doAcquireShared(arg); // <0:失败, 0:成功无剩余, >0:成功有剩余
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED); // 共享模式节点
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) { // 获取成功
setHeadAndPropagate(node, r); // 设置头节点并传播
p.next = null;
if (interrupted) selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed) cancelAcquire(node);
}
}
4.3.2 共享释放(releaseShared)
java
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); // 共享模式的关键:传播唤醒
return true;
}
return false;
}
// 共享模式的关键:传播唤醒机制
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))
continue; // CAS失败,重试
unparkSuccessor(h); // 唤醒一个后继
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 设置传播状态,确保唤醒继续传播
}
if (h == head) // 如果头节点没变,说明没有竞争,退出
break;
}
}
五、典型同步器的 AQS 实现分析
5.1 ReentrantLock(独占 + 可重入)
核心逻辑
- state = 0:未锁定
- state > 0:已锁定,值 = 重入次数
- 持有线程记录在 exclusiveOwnerThread
java
// 非公平锁 tryAcquire
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
公平 vs 非公平
| 类型 | 获取锁前是否检查队列 |
|---|---|
| 公平锁 | 是(hasQueuedPredecessors()) |
| 非公平锁 | 否(直接尝试 CAS) |
非公平锁性能更高(减少上下文切换),但可能导致线程饥饿。
5.2 CountDownLatch(共享模式)
核心逻辑
- state = N(初始计数)
- await()→ acquireShared(1)
- countDown()→ releaseShared(1)
java
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1; // 0 表示门已开
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0; // 归零则需唤醒所有等待者
}
}
一次性:state 不能重置,故不可重用。
5.3 Semaphore(共享模式)
核心逻辑
- state = permits(许可数)
- acquire() → 尝试减少许可
- release() → 增加许可
java
protected int tryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 || compareAndSetState(available, remaining))
return remaining;
}
}
六、基于AQS实现自定义同步器
6.1 简单互斥锁实现
java
/**
* 基于AQS实现的简单不可重入互斥锁
*/
public class SimpleMutex extends AbstractQueuedSynchronizer {
// 尝试获取锁:state从0变为1
@Override
protected boolean tryAcquire(int acquires) {
assert acquires == 1;
if (compareAndSetState(0, 1)) { // CAS操作保证原子性
setExclusiveOwnerThread(Thread.currentThread()); // 设置独占线程
return true;
}
return false;
}
// 释放锁:state从1变为0
@Override
protected boolean tryRelease(int releases) {
assert releases == 1;
if (getState() == 0)
throw new IllegalMonitorStateException("未持有锁");
setExclusiveOwnerThread(null); // 清除独占线程
setState(0); // 不需要CAS,因为只有持有者能释放
return true;
}
// 是否被当前线程独占
@Override
protected boolean isHeldExclusively() {
return getState() == 1 &&
getExclusiveOwnerThread() == Thread.currentThread();
}
// 对外提供的API
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return getState() == 1; }
// 使用示例
public static void main(String[] args) {
SimpleMutex lock = new SimpleMutex();
// 线程1
new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 1 acquired lock");
Thread.sleep(1000);
System.out.println("Thread 1 releasing lock");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}).start();
// 线程2
new Thread(() -> {
lock.lock();
try {
System.out.println("Thread 2 acquired lock");
} finally {
lock.unlock();
}
}).start();
}
}
6.2 简单CountDownLatch实现
java
/**
* 基于AQS的CountDownLatch实现
*/
public class SimpleCountDownLatch {
private final Sync sync;
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count); // 初始化计数器
}
int getCount() {
return getState();
}
// 共享获取:计数器为0时才能成功
@Override
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// 共享释放:计数器减1
@Override
protected boolean tryReleaseShared(int releases) {
for (;;) { // 循环CAS直到成功
int current = getState();
if (current == 0) return false; // 已经为0
int next = current - 1;
if (compareAndSetState(current, next)) {
return next == 0; // 返回true表示需要唤醒等待线程
}
}
}
}
public SimpleCountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void countDown() {
sync.releaseShared(1);
}
public long getCount() {
return sync.getCount();
}
}
6.3 公平锁 vs 非公平锁实现
java
// 非公平锁:直接尝试获取,不管等待队列
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) { // 不管队列,直接抢
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 重入逻辑
int nextc = c + acquires;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 公平锁:检查队列中是否有等待者
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 关键区别:检查是否有前驱节点在等待
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) { // 重入逻辑
int nextc = c + acquires;
if (nextc < 0) throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
七、总结
最近2个月陆陆续续的面试了不少公司,发现自己在许多方面还是有不足,在面试过程中查缺补漏,对一些问题会自己写成个人笔记,这篇关于AQS的就是前阵子回顾时,写好的,如有不足之处,还请见谅。