AQS原理详解

AQS 原理详解

💡 提示: 知识图谱
查看 AQS知识图谱 获取可视化知识结构

什么是 AQS?

AQS(AbstractQueuedSynchronizer,抽象队列同步器)是 Java 并发包 java.util.concurrent.locks 中的核心基础框架。

一句话理解

AQS 就像一个排队系统:想要获取资源的线程,如果资源被占用,就乖乖排队等待;资源释放后,排在最前面的线程被唤醒去获取资源。

为什么需要 AQS?

在 AQS 出现之前,如果我们想实现一个锁或者同步器,需要自己处理:

  • 线程的阻塞与唤醒
  • 等待队列的维护
  • 状态的管理

AQS 把这些通用逻辑抽取出来,我们只需要关注资源的获取和释放规则即可。


核心概念

1. State(同步状态)

AQS 使用一个 int 类型的变量 state 来表示同步状态:

1
private volatile int state;
同步器类型 state 含义
ReentrantLock 0 表示未锁定,>0 表示锁定次数(可重入)
Semaphore 表示可用的许可数量
CountDownLatch 表示需要等待的计数

2. CLH 队列(等待队列)

当线程获取资源失败时,会被包装成一个 Node 节点,加入到一个 FIFO 双向队列 中等待。

1
2
3
4
5
     +------+  prev +------+  prev +------+
head | | <---- | | <---- | | tail
| Node | | Node | | Node |
| | ----> | | ----> | |
+------+ next +------+ next +------+

3. Node 节点

每个等待的线程都被封装成一个 Node:

1
2
3
4
5
6
7
8
9
10
11
12
static final class Node {
// 等待状态
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 等待的线程
volatile Thread thread;
// 共享模式 or 独占模式
Node nextWaiter;
}

waitStatus 的取值:

常量名 含义
0 - 初始状态
1 CANCELLED 线程已取消
-1 SIGNAL 后继节点需要被唤醒
-2 CONDITION 在条件队列中等待
-3 PROPAGATE 共享模式下传播唤醒

两种资源共享模式

独占模式(Exclusive)

同一时刻只有一个线程能获取资源

典型实现:ReentrantLock

1
2
3
4
线程A获取锁 → 成功,执行业务
线程B获取锁 → 失败,进入队列等待
线程C获取锁 → 失败,进入队列等待
线程A释放锁 → 唤醒线程B

共享模式(Shared)

同一时刻可以有多个线程获取资源

典型实现:SemaphoreCountDownLatchReadWriteLock(读锁)

1
2
3
4
5
Semaphore(3) - 允许3个线程同时访问
线程A获取 → 成功,剩余2个许可
线程B获取 → 成功,剩余1个许可
线程C获取 → 成功,剩余0个许可
线程D获取 → 失败,进入队列等待

核心方法解析

需要子类实现的方法

AQS 采用模板方法模式,子类需要实现以下方法:

方法 模式 说明
tryAcquire(int) 独占 尝试获取资源
tryRelease(int) 独占 尝试释放资源
tryAcquireShared(int) 共享 尝试获取共享资源
tryReleaseShared(int) 共享 尝试释放共享资源
isHeldExclusively() - 是否独占持有

AQS 提供的模板方法

方法 说明
acquire(int) 独占式获取(会阻塞)
acquireInterruptibly(int) 独占式获取(可中断)
tryAcquireNanos(int, long) 独占式获取(带超时)
release(int) 独占式释放
acquireShared(int) 共享式获取
releaseShared(int) 共享式释放

独占模式源码分析

acquire() - 获取资源

1
2
3
4
5
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{是否被中断过}
    F -->|是| G[selfInterrupt 补上中断]
    F -->|否| H[返回]

addWaiter() - 加入等待队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// 快速尝试在尾部添加
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 快速添加失败,进入完整入队流程
enq(node);
return node;
}

acquireQueued() - 队列中等待获取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 自旋
final Node p = node.predecessor();
// 只有前驱是head才有资格尝试获取
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 判断是否需要阻塞
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}

关键点:

  1. 只有前驱节点是 head 的节点才有资格尝试获取资源
  2. 获取失败后会调用 LockSupport.park() 阻塞线程
  3. 被唤醒后继续自旋尝试获取

release() - 释放资源

1
2
3
4
5
6
7
8
9
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}

图解:完整的获取-释放流程

场景:三个线程竞争一把锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
初始状态:state = 0,队列为空

=== 线程A 获取锁 ===
1. tryAcquire(1) → CAS(0, 1) 成功
2. state = 1,线程A持有锁
3. 队列仍为空

=== 线程B 获取锁 ===
1. tryAcquire(1) → 失败(state=1)
2. addWaiter() → 创建Node(B),初始化队列

+------+ +------+
| head | ---> | B |
| 空 | | 等待 |
+------+ +------+

3. acquireQueued() → park阻塞

=== 线程C 获取锁 ===
1. tryAcquire(1) → 失败
2. addWaiter() → 创建Node(C)加入队尾

+------+ +------+ +------+
| head | ---> | B | ---> | C |
| 空 | | 等待 | | 等待 |
+------+ +------+ +------+

3. acquireQueued() → park阻塞

=== 线程A 释放锁 ===
1. tryRelease(1) → state = 0
2. unparkSuccessor(head) → 唤醒线程B
3. 线程B被唤醒,tryAcquire成功
4. 线程B成为新的head

+------+ +------+
| B | ---> | C |
| head | | 等待 |
+------+ +------+

实战:手写一个简单的互斥锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class SimpleLock {

private final Sync sync = new Sync();

// 继承AQS
private static class Sync extends AbstractQueuedSynchronizer {

// 尝试获取锁
@Override
protected boolean tryAcquire(int arg) {
// CAS 将 state 从 0 改为 1
if (compareAndSetState(0, 1)) {
// 设置当前线程为独占线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}

// 尝试释放锁
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
// 清除独占线程
setExclusiveOwnerThread(null);
// 释放锁
setState(0);
return true;
}

// 是否独占持有
@Override
protected boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
}

public void lock() {
sync.acquire(1);
}

public void unlock() {
sync.release(1);
}

public boolean tryLock() {
return sync.tryAcquire(1);
}
}

使用示例:

1
2
3
4
5
6
7
8
9
SimpleLock lock = new SimpleLock();

lock.lock();
try {
// 临界区代码
System.out.println("获取到锁,执行业务逻辑");
} finally {
lock.unlock();
}

AQS 的经典实现类

实现类 模式 说明
ReentrantLock 独占 可重入互斥锁
ReentrantReadWriteLock 共享+独占 读写锁
CountDownLatch 共享 倒计时门闩
Semaphore 共享 信号量
CyclicBarrier - 循环栅栏(内部用ReentrantLock)

核心要点总结

AQS 的设计精髓

  1. 模板方法模式:AQS 定义算法骨架,子类实现具体逻辑
  2. CLH 队列变体:高效的 FIFO 等待队列
  3. CAS + volatile:保证线程安全的无锁操作
  4. LockSupport:精确的线程阻塞/唤醒控制

记忆口诀

1
2
3
4
AQS三要素:状态、队列、CAS
独占看tryAcquire,共享看tryAcquireShared
获取失败进队列,前驱是head才能抢
释放资源唤后继,FIFO保公平

常见面试题

Q1:AQS 为什么使用双向链表?

  • 方便取消操作:取消节点时需要修改前驱的 next 指针
  • 方便判断前驱状态:只有前驱是 head 才能尝试获取资源
  • 方便从尾部遍历:某些场景需要从后向前遍历

Q2:为什么 head 节点是虚节点?

  • head 代表当前持有资源的线程(或初始空节点)
  • 真正等待的线程从 head.next 开始
  • 这样设计简化了边界条件处理

Q3:公平锁和非公平锁的区别?

类型 实现方式 特点
公平锁 tryAcquire 时先检查队列 严格 FIFO,吞吐量较低
非公平锁 tryAcquire 时直接 CAS 抢锁 可能插队,吞吐量较高
1
2
3
4
5
// 非公平锁的 tryAcquire(简化版)
if (state == 0 && CAS(0, 1)) return true;

// 公平锁的 tryAcquire(简化版)
if (state == 0 && !hasQueuedPredecessors() && CAS(0, 1)) return true;

延伸阅读

  • ReentrantLock 原理 - AQS 的典型独占实现
  • CountDownLatch 原理 - AQS 的典型共享实现
  • CAS 原理 - AQS 的底层原子操作

AQS原理详解
https://zmmmmy.github.io/2026/01/12/AQS原理详解/
作者
ZhiMy
发布于
2026年1月12日
许可协议