文章目录
- 一、AQS:何为抽象队列式同步器?
-
- [1.1 核心定义与定位](#1.1 核心定义与定位)
- [1.2 设计哲学与四大原则](#1.2 设计哲学与四大原则)
- 二、AQS的核心架构:三大支柱系统
-
- [2.1 状态管理系统(State Management)](#2.1 状态管理系统(State Management))
-
- [2.1.1 状态的原子性操作](#2.1.1 状态的原子性操作)
- [2.1.2 状态的多义性解释](#2.1.2 状态的多义性解释)
- [2.2 CLH队列系统(线程排队机制)](#2.2 CLH队列系统(线程排队机制))
-
- [2.2.1 队列节点设计](#2.2.1 队列节点设计)
- [2.2.2 队列操作流程](#2.2.2 队列操作流程)
- [2.2.3 入队操作源码解析](#2.2.3 入队操作源码解析)
- [2.3 模板方法系统(扩展机制)](#2.3 模板方法系统(扩展机制))
-
- [2.3.1 模板方法设计模式](#2.3.1 模板方法设计模式)
- [2.3.2 独占模式完整流程](#2.3.2 独占模式完整流程)
- 三、AQS在JUC中的核心地位
-
- [3.1 架构层次关系图](#3.1 架构层次关系图)
- [3.2 基于AQS的主要实现类](#3.2 基于AQS的主要实现类)
-
- [3.2.1 独占锁实现类](#3.2.1 独占锁实现类)
- [3.2.2 共享锁实现类](#3.2.2 共享锁实现类)
- 四、AQS的两种锁模式深度解析
-
- [4.1 独占模式(Exclusive Mode)](#4.1 独占模式(Exclusive Mode))
-
- [4.1.1 核心特点](#4.1.1 核心特点)
- [4.1.2 状态流转图](#4.1.2 状态流转图)
- [4.2 共享模式(Shared Mode)](#4.2 共享模式(Shared Mode))
-
- [4.2.1 核心特点](#4.2.1 核心特点)
- [4.2.2 共享模式获取流程](#4.2.2 共享模式获取流程)
- 五、AQS的条件队列(ConditionObject)
-
- [5.1 条件队列与同步队列的关系](#5.1 条件队列与同步队列的关系)
- [5.2 条件队列工作原理](#5.2 条件队列工作原理)
- 六、AQS性能优化与最佳实践
-
- [6.1 公平锁 vs 非公平锁性能对比](#6.1 公平锁 vs 非公平锁性能对比)
- [6.2 AQS的优化技巧](#6.2 AQS的优化技巧)
- [6.3 实战示例:自定义同步器](#6.3 实战示例:自定义同步器)
- 七、AQS在分布式环境中的启示
- 总结
在Java并发编程的世界里,有一个神秘而强大的框架默默支撑着众多并发工具的高效运行------它就是AbstractQueuedSynchronizer(AQS)。理解AQS,就等于掌握了Java并发编程的灵魂钥匙。本文将带你从设计哲学、核心原理到实战应用,全方位解析这个并发框架的奥秘。
一、AQS:何为抽象队列式同步器?
1.1 核心定义与定位
AbstractQueuedSynchronizer(AQS),即抽象的队列式同步器,是Java并发包(java.util.concurrent.locks)的核心基础框架。自JDK 1.5引入JUC包以来,AQS一直是Java并发编程体系的基石,其设计思想深刻影响了后续多版本JDK的并发实现。
AQS采用模板方法设计模式,提供了一套完整的多线程访问共享资源的同步器实现方案。开发者可以基于此框架快速构建各种类型的同步器,而无需从头实现复杂的线程排队、唤醒机制。
1.2 设计哲学与四大原则
AQS的成功源于其精妙的设计哲学,主要体现在以下四个核心原则:
- 分离关注点原则:将同步状态管理与线程排队机制分离,使得两者可以独立优化
- 模板方法模式:定义算法骨架,子类只需实现关键步骤的逻辑
- 性能优先原则:通过CAS操作和无锁设计最小化同步开销,最大化系统吞吐量
- 可扩展性原则:支持多种同步策略(独占/共享、公平/非公平、可重入/不可重入)
java
// AQS在Java并发体系中的位置
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 核心状态字段
private volatile int state;
// CLH队列头部(延迟初始化)
private transient volatile Node head;
// CLH队列尾部(延迟初始化)
private transient volatile Node tail;
// 核心模板方法
public final void acquire(int arg) { ... }
public final boolean release(int arg) { ... }
public final void acquireShared(int arg) { ... }
public final boolean releaseShared(int arg) { ... }
}
二、AQS的核心架构:三大支柱系统
2.1 状态管理系统(State Management)
2.1.1 状态的原子性操作
AQS通过一个volatile int state字段表示同步状态,所有状态变更都通过CAS(Compare-And-Swap)操作保证原子性:
java
// 状态操作方法(基于Unsafe类实现CAS)
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
static {
try {
// 获取state字段的内存偏移量
stateOffset = unsafe.objectFieldOffset(
AbstractQueuedSynchronizer.class.getDeclaredField("state"));
} catch (Exception ex) { throw new Error(ex); }
}
// CAS更新状态(核心中的核心)
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
// 获取和设置状态
protected final int getState() { return state; }
protected final void setState(int newState) { state = newState; }
2.1.2 状态的多义性解释
不同的同步器对state有不同的解释,这种设计的灵活性是AQS强大的关键:
| 同步器 | state含义 | 二进制表示示例 |
|---|---|---|
| ReentrantLock | 0=锁空闲,>0=重入次数 | 0000 0000 0000 0011 (重入3次) |
| Semaphore | 可用许可证数量 | 0000 0000 0000 0101 (5个许可) |
| CountDownLatch | 剩余倒数计数 | 0000 0000 0000 0010 (还需倒数2次) |
| ReentrantReadWriteLock | 高16位=读锁数,低16位=写锁重入 | 0000 0001 0000 0001 (1读锁,1写锁重入) |
2.2 CLH队列系统(线程排队机制)
2.2.1 队列节点设计
AQS使用一个变种的CLH(Craig, Landin, and Hagersten)队列作为等待队列,这是一个FIFO双向队列:
java
// 队列节点定义(静态内部类)
static final class Node {
// 节点模式
static final Node SHARED = new Node(); // 共享模式标记
static final Node EXCLUSIVE = null; // 独占模式标记
// 等待状态(waitStatus)
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; // 指向下一个等待条件的节点或SHARED标记
// 判断是否为共享模式
final boolean isShared() {
return nextWaiter == SHARED;
}
// 获取前驱节点(空值安全)
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
}
2.2.2 队列操作流程
线程获取锁失败
↓
创建Node节点(包含当前线程)
↓
通过CAS操作将节点加入队列尾部
↓
进入自旋检查循环
↓
前驱节点是头节点? → 是 → 再次尝试获取锁
↓ 否
检查前驱节点状态
↓
前驱节点状态为SIGNAL? → 是 → 调用LockSupport.park()挂起
↓ 否
清理CANCELLED状态的节点
↓
继续自旋检查
2.2.3 入队操作源码解析
java
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;
}
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)) {
t.next = node;
return t;
}
}
}
}
2.3 模板方法系统(扩展机制)
2.3.1 模板方法设计模式
AQS采用模板方法设计模式,定义了一套完整的同步算法骨架,具体实现细节留给子类:
java
// 模板方法(public final,子类不能覆盖)
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 1. 尝试获取(子类实现)
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 2. 加入队列等待
selfInterrupt(); // 3. 恢复中断状态
}
// 需要子类实现的钩子方法(protected)
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException(); // 默认抛异常
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
2.3.2 独占模式完整流程
java
// 独占模式获取锁的完整流程
public final void acquire(int arg) {
// 第一步:尝试快速获取(子类实现)
if (!tryAcquire(arg)) {
// 第二步:获取失败,创建节点加入等待队列
Node node = addWaiter(Node.EXCLUSIVE);
// 第三步:在队列中自旋等待
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor(); // 获取前驱节点
if (p == head && tryAcquire(arg)) { // 前驱是头节点且获取成功
setHead(node); // 设置自己为头节点
p.next = null; // 帮助GC
if (interrupted)
selfInterrupt();
return;
}
// 第四步:检查是否需要挂起
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
}
}
// 检查并挂起线程
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;
}
// 挂起线程并检查中断
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 挂起当前线程
return Thread.interrupted(); // 返回中断状态并清除中断标记
}
三、AQS在JUC中的核心地位
3.1 架构层次关系图
┌─────────────────────────────────────────────────────────┐
│ JUC并发工具层 (应用层) │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────────┐ │
│ │ Reentrant │ │ Semaphore │ │CountDown │ │Reentrant│ │
│ │ Lock │ │ │ │ Latch │ │ReadWrite│ │
│ │ │ │ │ │ │ │ Lock │ │
│ └─────┬────┘ └─────┬─────┘ └────┬─────┘ └────┬────┘ │
│ │ │ │ │ │
│ │ 独占模式 │ 共享模式 │ 共享模式 │ 读共享 │
│ │ │ │ │ 写独占 │
├────────┼──────────────┼─────────────┼─────────────┼──────┤
│ AQS核心框架层 (抽象层) │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 状态管理 + CLH队列 + 模板方法 = AQS │ │
│ │ • volatile int state (同步状态) │ │
│ │ • CLH队列 (线程排队) │ │
│ │ • 模板方法 (acquire/release) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────┤
│ 操作系统原语层 (底层) │
├─────────────────────────────────────────────────────────┤
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ LockSupport │ │ Unsafe │ │ CAS指令 │ │
│ │ (park/ │ │ (内存操作) │ │ (CPU原子操作)│ │
│ │ unpark) │ │ │ │ │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────┘
3.2 基于AQS的主要实现类
3.2.1 独占锁实现类
ReentrantLock(可重入锁)
- 公平锁:
new ReentrantLock(true)- 严格按照FIFO顺序获取锁 - 非公平锁:
new ReentrantLock(false)- 允许插队,吞吐量更高
java
// ReentrantLock中的Sync内部类继承AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
// 尝试获取锁的实现
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;
}
// 公平锁实现
static final class FairSync extends Sync {
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;
}
}
}
3.2.2 共享锁实现类
Semaphore(信号量)
java
public class Semaphore {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) { setState(permits); }
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining; // 返回剩余许可数
}
}
}
}
CountDownLatch(倒计时门闩)
java
public class CountDownLatch {
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) { setState(count); }
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; // 返回true表示门闩已打开
}
}
}
}
四、AQS的两种锁模式深度解析
4.1 独占模式(Exclusive Mode)
4.1.1 核心特点
- 同一时刻只允许一个线程持有锁
- 适用于互斥访问场景
- 典型实现:ReentrantLock、ReentrantReadWriteLock.WriteLock
4.1.2 状态流转图
初始状态 (state=0)
│
▼
┌─────────────────┐
│ 线程A获取锁 │◄─── tryAcquire(1)
│ (state=1) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 线程A重入 │◄─── tryAcquire(1)
│ (state=2) │
└────────┬────────┘
│ 释放1次 tryRelease(1)
▼
┌─────────────────┐
│ 线程A持有 │ (state=1)
└────────┬────────┘
│ 再次释放 tryRelease(1)
▼
┌─────────────────┐
│ 锁空闲 │ (state=0)
└─────────────────┘
4.2 共享模式(Shared Mode)
4.2.1 核心特点
- 同一时刻允许多个线程同时持有锁
- 适用于资源池、限流等场景
- 典型实现:Semaphore、CountDownLatch、ReentrantReadWriteLock.ReadLock
4.2.2 共享模式获取流程
java
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0) // 尝试获取
doAcquireShared(arg); // 加入队列等待
}
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);
}
}
// 设置头节点并向后传播唤醒
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
// 传播条件:propagate > 0 或 旧头节点为null或SIGNAL状态
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared(); // 唤醒后继共享节点
}
}
五、AQS的条件队列(ConditionObject)
5.1 条件队列与同步队列的关系
AQS除了同步队列外,还支持条件队列(ConditionObject),用于实现等待/通知机制:
java
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
// await()方法:将线程从同步队列移到条件队列
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); // 添加到条件队列
int savedState = fullyRelease(node); // 完全释放锁
int interruptMode = 0;
while (!isOnSyncQueue(node)) { // 不在同步队列中
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 被唤醒后重新竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
// signal()方法:将节点从条件队列移到同步队列
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
}
5.2 条件队列工作原理
同步队列 (等待锁) 条件队列 (等待条件)
┌─────────┐ ┌─────────┐
│ 头节点 │ │ first │
│ │ │ waiter │
├─────────┤ ├─────────┤
│ 节点A │◄──────────►│ 节点A │
├─────────┤ await() ├─────────┤
│ 节点B │ │ 节点B │
├─────────┤ signal() ├─────────┤
│ 节点C │───────────►│ 节点C │
└─────────┘ └─────────┘
六、AQS性能优化与最佳实践
6.1 公平锁 vs 非公平锁性能对比
| 特性 | 公平锁 | 非公平锁 |
|---|---|---|
| 获取顺序 | 严格FIFO | 允许插队 |
| 上下文切换 | 较多 | 较少 |
| 吞吐量 | 较低 | 较高(约10-100倍) |
| 饥饿问题 | 无 | 可能 |
| 适用场景 | 对响应时间敏感 | 高并发、高吞吐 |
6.2 AQS的优化技巧
- 减少CAS竞争:通过本地变量缓存状态减少volatile读取
- 延迟初始化:队列头尾节点延迟创建
- 自旋优化:在挂起前短暂自旋减少上下文切换
- 状态压缩:使用位运算在单个int中存储多个状态
6.3 实战示例:自定义同步器
java
// 实现一个简单的二进制锁(不可重入)
public class BinaryLock {
private static class Sync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int acquires) {
assert acquires == 1;
return compareAndSetState(0, 1);
}
protected boolean tryRelease(int releases) {
assert releases == 1;
if (getState() == 0) throw new IllegalMonitorStateException();
setState(0);
return true;
}
protected boolean isHeldExclusively() {
return getState() == 1;
}
}
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
}
七、AQS在分布式环境中的启示
虽然AQS是单机并发框架,但其设计思想对分布式锁设计有重要启示:
- 状态抽象:将锁状态抽象为可序列化的值
- 等待队列:分布式环境下的等待队列设计(如Redis List)
- CAS思想:分布式CAS通过版本号或乐观锁实现
- 可重入设计:记录持有者信息和重入次数
总结
AQS作为Java并发编程的基石,其精妙的设计体现了 Doug Lea 大师对并发问题的深刻理解。通过状态管理、CLH队列和模板方法三大支柱,AQS提供了一个高效、灵活且可扩展的同步器框架。
核心要点回顾:
- AQS通过
volatile int state+ CAS实现无锁状态管理 - CLH变体队列高效管理等待线程,减少竞争
- 模板方法模式分离不变部分和可变部分,提高扩展性
- 支持独占和共享两种模式,覆盖大部分并发场景
- 公平与非公平策略的权衡体现了性能与公平性的平衡
掌握AQS不仅有助于理解Java并发工具的实现原理,更能提升我们对并发编程本质的认识,为设计高性能、高可用的并发系统奠定坚实基础。
如需获取更多关于Java锁体系深度解析、AQS核心原理、JUC并发工具实战、分布式锁实现方案、锁性能优化秘籍、并发编程最佳实践等内容,请持续关注本专栏《Java并发锁机制全面精通》系列文章。
在Java并发编程的世界里,有一个神秘而强大的框架默默支撑着众多并发工具的高效运行------它就是AbstractQueuedSynchronizer(AQS)。理解AQS,就等于掌握了Java并发编程的灵魂钥匙。本文将带你从设计哲学、核心原理到实战应用,全方位解析这个并发框架的奥秘。