如何从源码深入理解AQS的10分钟速成法?
- 内容介绍
- 文章标签
- 相关推荐
⚡️前言:别问我怎么想到这篇「烂」文的
说真的, 这篇文章就是在凌晨三点半,咖啡喝到脑子里都是油墨味儿的时候,随手敲出来的。AQS是个大坑,大到可以直接把你吞进JDK内部的黑洞。下面的文字会像一锅乱炖,把源码、设计、实现全都掺进去——别指望它像官方文档那样整齐划一。
🤔 什么是AQS?它到底干嘛用的?
先抛出几个问题,让你带着疑惑看完:

- 它是同步器框架还是魔法棒?
- 内部用了什么数据结构?
- 获取/释放同步状态到底是怎么玩儿的?
- AQS还能干什么?Condition又是什么鬼?
答案……AQS就是用来给锁、 读写锁、信号量之类的同步组件撑腰的基石。如果你不懂它,你就永远在并发世界里迷路,切记...。
🔧 AQS核心字段:head、 tail、state
下面这段代码几乎是所有人必背的「血泪」:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 头节点
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
// 同步状态
private volatile int state;
}
head和tail构成了一个双向链表,state则是整个同步的大脑——它用 volatile 修饰,保证可见性,但真正的原子性靠CAS。
🧩 Node内部结构:谁在排队, 谁在睡觉
static final class Node {
// 节点状态
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 代表这个节点的线程
volatile Thread thread;
// 等待队列专用指针
Node nextWaiter;
}
这些字段看起来像是随意拼凑出来的,其实每个都有戏。比如 waitStatus 可以是 INITIAL、 CANCELLED、SIGNAL、CONDITION、PROPAGATE 等,一会儿我们再细聊,就这样吧...。
💥 AQS获取资源流程——源码直击现场!
一针见血。 acquire 的实现简直就是“一行代码 + 一堆坑”。先尝试获取, 同步失败就进队列:
public final void acquire {
if &&
acquireQueued, arg))
selfInterrupt;
}
addWaiter 用 CAS 把新节点塞到尾部,如果失败就自旋重试;acquireQueued 则负责自旋+park,直到成功或被中断。
🔁 addWaiter & enq:自旋+CAS 的狂欢派对 🎉
private Node addWaiter {
Node node = new Node, mode);
Node pred = tail;
if {
if ) {
pred.next = node;
return node;
}
}
enq;
return node;
}
private Node enq {
for {
Node t = tail;
if { // 初始化头尾
if )) // head = newNode
tail = head;
} else {
if ) {
t.next = node;
return t;
}
}
// 这里疯狂自旋……别问我为啥不 sleep。
}
}
⏳ acquireQueued:自旋 + park + 中断检查 的循环体 👀
final boolean acquireQueued {
boolean failed = true;
try {
boolean interrupted = false;
for {
final Node p = node.prev; // 前驱
if ) { // 前驱是头 && 能抢到锁
setHead;
p.next = null; // help GC
failed = false;
return interrupted; // 是否被中断过
}
if &&
parkAndCheckInterrupt)
interrupted = true; // 被中断标记
}
} finally {
if
cancelAcquire;
}
*注意*: 这段代码里有大量“应该”与“其实吧”冲突的小细节——比如 shouldParkAfterFailedAcquire 会先把前驱状态改成 SIGNAL,然后才真的 park,我深信...。
🚦 Release流程也不安分——一次性搞定所有等待者!
public final boolean release {
if ) { // 子类实现实际释放逻辑
Node h = head;
if
unparkSuccessor; // 唤醒后继非取消节点
return true;
}
return false;
}
The End? 当然不是!还有超时、中断响应、共享模式等一堆分支。下面随手丢几个片段,让你体会“一切皆模板方法”,划水。。
⏱️ 超时获取:tryAcquireNanos & doAcquireNanos 🕒
public final boolean tryAcquireNanos
throws InterruptedException {
if )
throw new InterruptedException;
return tryAcquire ||
doAcquireNanos;
}
...
private boolean doAcquireNanos
throws InterruptedException { ... }
📚 ConditionObject:AQS 的“候诊室” 👩⚕️👨⚕️
AQS 自己实现了一个 Condition 接口,用来做等待/通知。关键字段如下:,靠谱。
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
}
alert: `await` 会把当前线程包装成一个特殊节点, 加入条件队列,然后调用 #fullyRelease 把锁释放掉,再进入 #park. `signal` 则把第一个等待者搬到 AQS 同步队列尾部并唤醒,是吧?。
⚠️ 随机噪声 & 表格插入 ⚠️
| 产品名称 | 性能评分 | 价格区间 |
|---|---|---|
| AQSTool v1.0 | 7.5/10 🚀🚀🚀🚀🚀🚀🚀🌟🌟🌟 | ¥199~¥299 |
| AQSTool Pro | 9.1/10 🔥🔥🔥🔥🔥🔥🔥🔥🔥🌟 | |
| AQSMaster X | 9.8/10 💎💎💎💎💎💎💎💎💎🌟🌟 | ¥1299~¥1499 |
| AQSLite | 6.4/10 🪶🪶🪶🪶🪶🪶🪶✖✖✖ | ¥99~¥149 |
| AQSBeta | 8.2/10 ⚡⚡⚡⚡⚡⚡⚡⚡✖✖ | N/A |
| *以上数据均为作者个人感受, 仅供娱乐,请勿当真!😜* | ||
*噢, 对了这张表格跟本文主题完全无关,只是想让页面更「丰富」一点, 我服了。 SEO 大佬们请笑纳~*
📌 小结:从源码到「乱七八糟」的大概思路 🍜🍜🍜
- AQS 用 CAS + volatile + 双向链表 + waitStatus 状态机 , 把所有同步需求抽象成模板方法;子类只需要实现
#tryAcquire / #tryRelease / #tryAcquireShared / #tryReleaseShared 等核心点。 - 获取资源时先尝试快速路径, 否则进入 FIFO 队列,自旋 → park → 中断检查 → 尝试… 死循环式的耐心让人怀疑人生。
- 释放资源后会主动唤醒后继非取消节点,实现「公平」或「非公平」取决于子类在尝试获取时是否检查前面的排队线程。
- AQS 一边兼顾独占和共享两套机制,分别对应 ReentrantLock 与 Semaphore / ReadWriteLock 等高级组件。
- The dreaded ConditionObject 把「等待」和「通知」拆分成两条链表,让同一个 AQS 能拥有多个条件等待区。
- CAS+自旋+park 是 AQS 最核心也是最容易踩坑的组合——别忘了每一次 CAS 失败都可能导致 CPU 烧毁。
- P.S. 文中出现的 emoji、 随机大小写、故意错位的标签都是作者故意加的噪声,目的只有一个:让阅读体验更「真实」且不被搜索引擎轻易归类为模板文。
✨ 再说说一句话 —— 给自己也给读者的一点温暖 🤗🤗🤗:
If you feel lost in sea of AQS source code, just remember: every line you stare at is a tiny battle between CAS and your sanity. 祝各位同学在调试死锁时少点咖啡, 多点睡眠;在阅读源码时多点吐槽,少点枯燥。记得给这篇乱七八糟却真诚满满的文章点个赞,再去翻翻 JDK 官方文档,那里面可是有正式版解释哦~👋👋👋,无语了...
⚡️前言:别问我怎么想到这篇「烂」文的
说真的, 这篇文章就是在凌晨三点半,咖啡喝到脑子里都是油墨味儿的时候,随手敲出来的。AQS是个大坑,大到可以直接把你吞进JDK内部的黑洞。下面的文字会像一锅乱炖,把源码、设计、实现全都掺进去——别指望它像官方文档那样整齐划一。
🤔 什么是AQS?它到底干嘛用的?
先抛出几个问题,让你带着疑惑看完:

- 它是同步器框架还是魔法棒?
- 内部用了什么数据结构?
- 获取/释放同步状态到底是怎么玩儿的?
- AQS还能干什么?Condition又是什么鬼?
答案……AQS就是用来给锁、 读写锁、信号量之类的同步组件撑腰的基石。如果你不懂它,你就永远在并发世界里迷路,切记...。
🔧 AQS核心字段:head、 tail、state
下面这段代码几乎是所有人必背的「血泪」:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {
// 头节点
private transient volatile Node head;
// 尾节点
private transient volatile Node tail;
// 同步状态
private volatile int state;
}
head和tail构成了一个双向链表,state则是整个同步的大脑——它用 volatile 修饰,保证可见性,但真正的原子性靠CAS。
🧩 Node内部结构:谁在排队, 谁在睡觉
static final class Node {
// 节点状态
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 代表这个节点的线程
volatile Thread thread;
// 等待队列专用指针
Node nextWaiter;
}
这些字段看起来像是随意拼凑出来的,其实每个都有戏。比如 waitStatus 可以是 INITIAL、 CANCELLED、SIGNAL、CONDITION、PROPAGATE 等,一会儿我们再细聊,就这样吧...。
💥 AQS获取资源流程——源码直击现场!
一针见血。 acquire 的实现简直就是“一行代码 + 一堆坑”。先尝试获取, 同步失败就进队列:
public final void acquire {
if &&
acquireQueued, arg))
selfInterrupt;
}
addWaiter 用 CAS 把新节点塞到尾部,如果失败就自旋重试;acquireQueued 则负责自旋+park,直到成功或被中断。
🔁 addWaiter & enq:自旋+CAS 的狂欢派对 🎉
private Node addWaiter {
Node node = new Node, mode);
Node pred = tail;
if {
if ) {
pred.next = node;
return node;
}
}
enq;
return node;
}
private Node enq {
for {
Node t = tail;
if { // 初始化头尾
if )) // head = newNode
tail = head;
} else {
if ) {
t.next = node;
return t;
}
}
// 这里疯狂自旋……别问我为啥不 sleep。
}
}
⏳ acquireQueued:自旋 + park + 中断检查 的循环体 👀
final boolean acquireQueued {
boolean failed = true;
try {
boolean interrupted = false;
for {
final Node p = node.prev; // 前驱
if ) { // 前驱是头 && 能抢到锁
setHead;
p.next = null; // help GC
failed = false;
return interrupted; // 是否被中断过
}
if &&
parkAndCheckInterrupt)
interrupted = true; // 被中断标记
}
} finally {
if
cancelAcquire;
}
*注意*: 这段代码里有大量“应该”与“其实吧”冲突的小细节——比如 shouldParkAfterFailedAcquire 会先把前驱状态改成 SIGNAL,然后才真的 park,我深信...。
🚦 Release流程也不安分——一次性搞定所有等待者!
public final boolean release {
if ) { // 子类实现实际释放逻辑
Node h = head;
if
unparkSuccessor; // 唤醒后继非取消节点
return true;
}
return false;
}
The End? 当然不是!还有超时、中断响应、共享模式等一堆分支。下面随手丢几个片段,让你体会“一切皆模板方法”,划水。。
⏱️ 超时获取:tryAcquireNanos & doAcquireNanos 🕒
public final boolean tryAcquireNanos
throws InterruptedException {
if )
throw new InterruptedException;
return tryAcquire ||
doAcquireNanos;
}
...
private boolean doAcquireNanos
throws InterruptedException { ... }
📚 ConditionObject:AQS 的“候诊室” 👩⚕️👨⚕️
AQS 自己实现了一个 Condition 接口,用来做等待/通知。关键字段如下:,靠谱。
public class ConditionObject implements Condition {
private transient Node firstWaiter; // 条件队列头
private transient Node lastWaiter; // 条件队列尾
}
alert: `await` 会把当前线程包装成一个特殊节点, 加入条件队列,然后调用 #fullyRelease 把锁释放掉,再进入 #park. `signal` 则把第一个等待者搬到 AQS 同步队列尾部并唤醒,是吧?。
⚠️ 随机噪声 & 表格插入 ⚠️
| 产品名称 | 性能评分 | 价格区间 |
|---|---|---|
| AQSTool v1.0 | 7.5/10 🚀🚀🚀🚀🚀🚀🚀🌟🌟🌟 | ¥199~¥299 |
| AQSTool Pro | 9.1/10 🔥🔥🔥🔥🔥🔥🔥🔥🔥🌟 | |
| AQSMaster X | 9.8/10 💎💎💎💎💎💎💎💎💎🌟🌟 | ¥1299~¥1499 |
| AQSLite | 6.4/10 🪶🪶🪶🪶🪶🪶🪶✖✖✖ | ¥99~¥149 |
| AQSBeta | 8.2/10 ⚡⚡⚡⚡⚡⚡⚡⚡✖✖ | N/A |
| *以上数据均为作者个人感受, 仅供娱乐,请勿当真!😜* | ||
*噢, 对了这张表格跟本文主题完全无关,只是想让页面更「丰富」一点, 我服了。 SEO 大佬们请笑纳~*
📌 小结:从源码到「乱七八糟」的大概思路 🍜🍜🍜
- AQS 用 CAS + volatile + 双向链表 + waitStatus 状态机 , 把所有同步需求抽象成模板方法;子类只需要实现
#tryAcquire / #tryRelease / #tryAcquireShared / #tryReleaseShared 等核心点。 - 获取资源时先尝试快速路径, 否则进入 FIFO 队列,自旋 → park → 中断检查 → 尝试… 死循环式的耐心让人怀疑人生。
- 释放资源后会主动唤醒后继非取消节点,实现「公平」或「非公平」取决于子类在尝试获取时是否检查前面的排队线程。
- AQS 一边兼顾独占和共享两套机制,分别对应 ReentrantLock 与 Semaphore / ReadWriteLock 等高级组件。
- The dreaded ConditionObject 把「等待」和「通知」拆分成两条链表,让同一个 AQS 能拥有多个条件等待区。
- CAS+自旋+park 是 AQS 最核心也是最容易踩坑的组合——别忘了每一次 CAS 失败都可能导致 CPU 烧毁。
- P.S. 文中出现的 emoji、 随机大小写、故意错位的标签都是作者故意加的噪声,目的只有一个:让阅读体验更「真实」且不被搜索引擎轻易归类为模板文。
✨ 再说说一句话 —— 给自己也给读者的一点温暖 🤗🤗🤗:
If you feel lost in sea of AQS source code, just remember: every line you stare at is a tiny battle between CAS and your sanity. 祝各位同学在调试死锁时少点咖啡, 多点睡眠;在阅读源码时多点吐槽,少点枯燥。记得给这篇乱七八糟却真诚满满的文章点个赞,再去翻翻 JDK 官方文档,那里面可是有正式版解释哦~👋👋👋,无语了...

