引言:AQS在Java并发体系中的核心地位
AQS(AbstractQueuedSynchronizer)作为Java并发包的底层基石,是理解ReentrantLock、Semaphore等同步工具的关键。
在Java架构师面试中,AQS的原理与应用是高频考点,掌握其核心机制对理解JUC包和构建高并发系统至关重要。
本文将从原理、应用、源码到Spring生态实践进行全面解析,帮助读者系统掌握这一核心技术。
一、AQS核心原理深度剖析
1.1 AQS的架构设计与核心组件
AQS是Java并发包中用于构建锁和同步器的抽象框架,其设计包含三大核心组件:
1.1.1 三大核心组件详解
组件名称 | 技术实现 | 核心作用 |
---|---|---|
状态变量state | volatile int,通过CAS操作更新 | 存储同步状态,不同场景含义不同(如锁重入次数、信号量许可数等) |
CLH等待队列 | 双向链表,由Node节点组成 | 管理等待线程的FIFO顺序,实现线程阻塞与唤醒机制 |
模板方法 | 抽象方法需子类实现 | 定义同步逻辑接口(如tryAcquire、tryRelease等),实现模板方法模式 |
1.1.2 Node节点核心字段解析
static final class Node {
// 节点状态:CANCELLED(1)、SIGNAL(-1)、CONDITION(-2)、PROPAGATE(-3)
volatile int waitStatus;
// 前驱节点引用
volatile Node prev;
// 后继节点引用
volatile Node next;
// 关联的线程
volatile Thread thread;
// 条件队列中的后继节点
Node nextWaiter;
}
1.1.3 CLH队列结构示意图
CLH队列关键特性说明
- 双向链表结构:
- 每个节点包含
prev
和next
指针 - 头节点(Head)的prev为null
- 尾节点(Tail)的next为null
- 每个节点包含
- 节点内部结构:
static final class Node { volatile int waitStatus; // 等待状态 volatile Node prev; // 前驱节点 volatile Node next; // 后继节点 volatile Thread thread; // 关联线程 Node nextWaiter; // 条件队列链接 }
- 等待状态(waitStatus):
SIGNAL(-1)
:后继节点需要唤醒CANCELLED(1)
:线程已取消CONDITION(-2)
:在条件队列中0
:初始状态
- 队列操作:
- 入队:尾插法(CAS更新Tail)
// AQS中的入队代码 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); // CAS失败时自旋入队 return node; }
- 出队:头节点释放后唤醒后继
// 唤醒后继节点 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); }
- 入队:尾插法(CAS更新Tail)
1.2 AQS工作流程详解(独占模式)
1.2.1 核心执行流程
1.2.2 关键机制解析
- CAS无锁操作:
// AQS更新state的核心方法 protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
- 通过Unsafe类直接操作内存,确保原子性
- stateOffset通过
objectFieldOffset
获取字段偏移量
- 线程阻塞与唤醒:
- 阻塞:
LockSupport.park(this)
- 唤醒:
LockSupport.unpark(thread)
- 基于Unsafe的park/unpark,比Object.wait/notify更高效
- 阻塞:
- 队列管理:
- 入队:CAS尾插法,保证线程安全
- 出队:头节点释放后,将后继节点设为新头节点
二、AQS的同步模式与核心方法
2.1 独占模式(Exclusive Mode)
2.1.1 核心特性
- 同一时刻仅允许一个线程获取资源
- 典型实现:ReentrantLock、ReentrantReadWriteLock.WriteLock
- 关键方法:
acquire(int arg)
:获取资源,失败则入队等待release(int arg)
:释放资源,唤醒后继节点
2.1.2 状态流转图
2.2 共享模式(Shared Mode)
2.2.1 核心特性
- 允许多个线程同时获取资源
- 典型实现:Semaphore、CountDownLatch、ReentrantReadWriteLock.ReadLock
- 关键方法:
acquireShared(int arg)
:获取共享资源,失败入队releaseShared(int arg)
:释放共享资源,可能唤醒后继