TL;DR(30 秒扫完)
- AQS = AbstractQueuedSynchronizer(抽象队列同步器),位于
java.util.concurrent.locks包 - 两大核心数据结构:
volatile int state(同步状态)+ CLH 双向队列(head/tail) - 两种工作模式:独占(tryAcquire/tryRelease)+ 共享(tryAcquireShared/tryReleaseShared)
- 模板方法模式:父类负责排队调度,子类负责判断"能不能拿到锁"
- 主要子类:ReentrantLock(state=重入次数)、Semaphore(state=剩余许可)、CountDownLatch(state=倒计时)
关键结论
结论 AAQS 是 JUC 并发包的基座,ReentrantLock、Semaphore、CountDownLatch、CyclicBarrier、ReadWriteLock 都直接或间接继承它
结论 B两大核心数据结构——
volatile int state 记录同步状态(含义由子类决定)+ CLH 双向队列用于排队等待结论 C两种工作模式——独占模式(一次一个线程,如 ReentrantLock)+ 共享模式(多个线程可同时,如 ReadLock、Semaphore)
结论 D模板方法模式是 AQS 设计优雅的关键——父类负责排队、唤醒、调度,子类只实现"拿到锁/释放锁"的判断逻辑
完整讲解(费曼四步)
STEP 1 · 概念
AQS 全称 AbstractQueuedSynchronizer(抽象队列同步器),是 JUC 并发包的基座。它通过一个volatile int state 变量和 CLH 双向队列,把"如何判断能不能拿到锁"和"如何调度等待线程"解耦,让子类只需实现判断逻辑。
STEP 2 · 大白话
类比:AQS 就像一个"排队取号系统"。>
- state 是"当前柜台的服务状态"(比如"剩余许可数"或"重入次数")
- CLH 队列是"等候区",取不到号的线程挂在这里
- 独占模式 = "一次只服务一个人"(如 ReentrantLock)
- 共享模式 = "多人同时服务"(如 Semaphore 限流 5 个)
- 模板方法 = "系统负责叫号排队,店员只负责判断能不能服务"
STEP 3 · 底层
两大核心数据结构
| 结构 | 类型 | 含义 |
|---|---|---|
| state | volatile int | 同步状态,含义由子类决定。ReentrantLock=重入次数、Semaphore=剩余许可、CountDownLatch=倒计时 |
| CLH 双向队列 | Treiber 队列变种 | 获取锁失败的线程被封装成 Node 节点挂到队尾。head=头节点(持锁线程),tail=尾指针 |
prev/next:双向链表指针waitStatus:节点状态(SIGNAL/WCANCELED/PROPAGATE/CONDITION)thread:等待的线程nextWaiter:条件队列节点(共享/独占标记)
两种工作模式
| 模式 | 含义 | 子类实现 | 典型例子 |
|---|---|---|---|
| 独占模式 | 一次只有一个线程能持有 | tryAcquire / tryRelease | ReentrantLock、ReadWriteLock 写锁 |
| 共享模式 | 多个线程可同时持有 | tryAcquireShared / tryReleaseShared | ReadLock、Semaphore、CountDownLatch |
独占 vs 共享的核心区别
| 维度 | 独占模式 | 共享模式 |
|---|---|---|
| 允许进入的线程数 | 1 个 | 多个 |
| 模板方法 | tryAcquire(int) | tryAcquireShared(int) |
state 语义 | 0=空闲,非0=持有次数 | 允许多线程共享 state(如剩余许可) |
| 释放方法 | tryRelease(int) | tryReleaseShared(int) |
| 返回语义 | 独占获得/释放(boolean) | 共享获得/释放(返回剩余量) |
ReadWriteLock = 双 AQS 实例组合
public class ReentrantReadWriteLock {
private final Sync sync = new Sync(); // ← 单一 Sync 内部维护 readCount + writeHold
// 但从 AQS 使用角度,读锁和写锁表现为两个"独立的同步语义"
}
- WriteLock:独占模式,
state高 16 位存写锁重入次数 - ReadLock:共享模式,
state低 16 位存读锁持有数 - 一个
Sync内同时维护两种语义——但对外表现为两个 AQS 实例 - 面试口径:ReadWriteLock 是"独占+共享"的组合,写独占 + 读共享
主要子类及 state 含义
| 子类 | state 含义 | 模式 |
|---|---|---|
| ReentrantLock | 重入次数(0=未持有,N=重入 N 次) | 独占 |
| ReentrantReadWriteLock | 高 16 位=写锁重入次数,低 16 位=读锁持有数 | 混合(双 AQS) |
| Semaphore | 剩余许可数 | 共享 |
| CountDownLatch | 倒计时(0=到达终点) | 共享 |
| CyclicBarrier | 参与线程数 | 共享 |
| Phaser | 注册/到达计数 | 共享 |
独占/共享组件完整清单
| 组件 | 模式 |
|---|---|
| ReentrantLock | 独占 |
| ReentrantReadWriteLock.WriteLock | 独占 |
| ReentrantReadWriteLock.ReadLock | 共享 |
| Semaphore | 共享 |
| CountDownLatch | 共享 |
| CyclicBarrier | 共享 |
| Phaser | 共享 |
| StampedLock(读优化) | 混合(读无锁化) |
常见误区(独占/共享专项)
- 误区:MySQL 读写锁就是 AQS 独占/共享 → 错误。MySQL 读写锁是混合模式,等价于 Java 的 ReadWriteLock(双 AQS 组合)
- 误区:共享模式的 state 表示"持有锁的线程数" → 错误。共享模式的 state 语义由子类决定,Semaphore=剩余许可、CountDownLatch=倒计时
模板方法模式
父类 AQS:
- acquire() → tryAcquire() → 成功则设置 owner,失败则入队
- release() → tryRelease() → 唤醒后继线程
- 负责:排队、唤醒、调度
子类 ReentrantLock:
- tryAcquire() → 判断 state 是否为 0 或是否已持有
- tryRelease() → state-- 判断是否为 0
- 只负责:判断逻辑
主要子类及 state 含义
| 子类 | state 含义 | 模式 |
|---|---|---|
| ReentrantLock | 重入次数(0=未持有,N=重入 N 次) | 独占 |
| ReentrantReadWriteLock | 高 16 位=写锁重入次数,低 16 位=读锁持有数 | 混合 |
| Semaphore | 剩余许可数 | 共享 |
| CountDownLatch | 倒计时(0=到达终点) | 共享 |
| CyclicBarrier | 参与线程数 | 共享 |
与 synchronized 对比
| 维度 | synchronized | AQS (ReentrantLock) |
|---|---|---|
| 实现 | JVM 内置 | 用户态 |
| 可中断 | ✗ | ✓(lockInterruptibly) |
| 可超时 | ✗ | ✓(tryLock(timeout)) |
| 公平性 | 非公平锁 | 可选(isFair 构造参数) |
| 多条件变量 | ✗(单 waitSet) | ✓(多个 Condition) |
| 性能 | 低竞争更快 | 高竞争更快(可优化调度) |
STEP 4 · 简化
一句话总结:AQS = volatile int state + CLH 双向队列 + 独占/共享两种模式 + 模板方法。父类负责排队调度,子类只负责判断。ReentrantLock/Semaphore/CountDownLatch 都是它的子类。
常见误区
AQS 就是 ReentrantLock
AQS 是基座,ReentrantLock 是子类之一
AQS 全称不清楚
AbstractQueuedSynchronizer(抽象队列同步器)
阻塞队列是 AQS 的核心功能
阻塞队列是用 AQS 实现的组件,不是 AQS 本身
AQS 只有独占模式
有独占和共享两种模式
延伸追问
AQS 为什么用双向链表而不是单向?
释放锁时要唤醒前驱的后继节点(独占)或所有后继(共享),单向链表无法高效遍历
ReentrantLock 怎么实现可重入?
state 记录重入次数,同一线程再次获取时 state++,释放时 state--,直到 0 才真正释放
公平锁 vs 非公平锁的区别?
公平锁 tryAcquire 时先看 hasQueuedPredecessors(队列里有没有更老的线程);非公平锁直接 CAS 抢。非公平锁吞吐更高,公平锁避免饥饿
速查表
AQS = AbstractQueuedSynchronizer
两大核心:volatile int state + CLH 双向队列
两种模式:独占(tryAcquire/tryRelease)+ 共享(tryAcquireShared/tryReleaseShared)
模板方法:父类调度,子类判断
子类 state 含义:
ReentrantLock = 重入次数
Semaphore = 剩余许可数
CountDownLatch = 倒计时
ReadLock = 读锁持有数(共享模式)
vs synchronized:
synchronized = JVM 内置、不可中断、不可超时、非公平
AQS = 用户态、可中断、可超时、可选公平
Anki 候选卡片
Q: AQS 全称?
A: AbstractQueuedSynchronizer(抽象队列同步器)
Q: AQS 的两大核心数据结构?
A: volatile int state(同步状态)+ CLH 双向队列(head/tail)
Q: AQS 的两种工作模式?
A: 独占模式(tryAcquire/tryRelease,如 ReentrantLock)+ 共享模式(tryAcquireShared/tryReleaseShared,如 ReadLock)
Q: ReentrantLock 怎么实现可重入?
A: state 记录重入次数,同线程再次获取时 state++,释放时 state--,直到 0 才真正释放
Q: AQS 和 synchronized 的区别?
A: synchronized=JVM 内置、不可中断、不可超时;AQS=用户态、可中断、可超时、可选公平
关联题目
关联知识
AQS=AbstractQueuedSynchronizer;两大核心 volatile int state + CLH 双向队列;两种模式独占/共享;模板方法父子协作;ReadWriteLock 是双 AQS 实例组合
AQS 像排队取号系统:state 是柜台服务状态,CLH 队列是等候区,独占=一次服务一人(ReentrantLock),共享=多人同时服务(Semaphore)
✦ 记 忆 口 诀 ✦
AQS=AbstractQueuedSynchronizer;volatile int state + CLH 双向队列;独占/共享两种模式;模板方法=父调度子判断;ReadWriteLock 双 AQS 组合
关键可视化
AQS 架构:state + CLH 队列
flowchart TB AQS["AQS = AbstractQueuedSynchronizer"] --> S["volatile int state<br/>同步状态<br/>含义由子类决定"] AQS --> Q["CLH 双向队列<br/>head/tail<br/>等待线程 Node 链表"] Q --> N["Node 结构<br/>prev/next<br/>waitStatus<br/>thread<br/>nextWaiter"] AQS -.模板方法.-> T["父类负责排队调度<br/>子类实现 tryAcquire/tryRelease"]
独占模式 vs 共享模式
flowchart TB AQS["AQS 两种模式"] --> EX["独占模式<br/>一次一个线程<br/>tryAcquire/tryRelease<br/>state=持有次数<br/>返回 boolean"] AQS --> SH["共享模式<br/>多线程同时<br/>tryAcquireShared/tryReleaseShared<br/>state=剩余许可<br/>返回 int"] EX --> C1["ReentrantLock"] EX --> C2["WriteLock"] SH --> C3["ReadLock"] SH --> C4["Semaphore"] SH --> C5["CountDownLatch"]
主要子类 state 含义
flowchart TB
subgraph SUB["AQS 子类"]
R["ReentrantLock<br/>state=重入次数<br/>(0=未持有,N=重入 N 次)<br/>独占模式"]
RL["ReentrantReadWriteLock<br/>state 高 16 位=写锁重入次数<br/>state 低 16 位=读锁持有数<br/>双 AQS 组合"]
SEM["Semaphore<br/>state=剩余许可数<br/>共享模式"]
CDL["CountDownLatch<br/>state=倒计时(0=到达终点)<br/>共享模式"]
CYC["CyclicBarrier<br/>state=参与线程数<br/>共享模式"]
PH["Phaser<br/>state=注册/到达计数<br/>共享模式"]
end模板方法模式
flowchart TB
subgraph P["父类 AQS"]
P1["acquire() → tryAcquire() → 成功设 owner<br/>失败入队"]
P2["release() → tryRelease() → 唤醒后继"]
P3["负责:排队、唤醒、调度"]
end
subgraph C["子类 ReentrantLock"]
C1["tryAcquire() → 判断 state 是否为 0<br/>或是否已持有"]
C2["tryRelease() → state-- 判断是否为 0"]
C3["只负责:判断逻辑"]
end
P -.模板方法.-> Csynchronized vs AQS (ReentrantLock)
flowchart TB
subgraph SYN["synchronized"]
S1["JVM 内置"]
S2["不可中断"]
S3["不可超时"]
S4["非公平锁"]
S5["单 waitSet"]
S6["低竞争更快"]
end
subgraph AQS["AQS (ReentrantLock)"]
A1["用户态"]
A2["可中断 lockInterruptibly"]
A3["可超时 tryLock(timeout)"]
A4["可选公平 isFair"]
A5["多 Condition"]
A6["高竞争更快"]
end知识关系
🔄 延伸(Extends)
jmm-volatile-cas
🎯 概念
📏 规则
⚠️ 误区
🔍 追问
✨ 口诀
共 0 张卡,点击翻面