Products
GG网络技术分享 2026-04-17 06:58 0
说真的, 这篇文章就是在凌晨三点半,咖啡喝到脑子里都是油墨味儿的时候,随手敲出来的。AQS是个大坑,大到可以直接把你吞进JDK内部的黑洞。下面的文字会像一锅乱炖,把源码、设计、实现全都掺进去——别指望它像官方文档那样整齐划一。
先抛出几个问题,让你带着疑惑看完:

答案……AQS就是用来给锁、 读写锁、信号量之类的同步组件撑腰的基石。如果你不懂它,你就永远在并发世界里迷路,切记...。
下面这段代码几乎是所有人必背的「血泪」:
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。
static final class Node {
// 节点状态
volatile int waitStatus;
// 前驱节点
volatile Node prev;
// 后继节点
volatile Node next;
// 代表这个节点的线程
volatile Thread thread;
// 等待队列专用指针
Node nextWaiter;
}
这些字段看起来像是随意拼凑出来的,其实每个都有戏。比如 waitStatus 可以是 INITIAL、 CANCELLED、SIGNAL、CONDITION、PROPAGATE 等,一会儿我们再细聊,就这样吧...。
一针见血。 acquire 的实现简直就是“一行代码 + 一堆坑”。先尝试获取, 同步失败就进队列:
public final void acquire {
if &&
acquireQueued, arg))
selfInterrupt;
}
addWaiter 用 CAS 把新节点塞到尾部,如果失败就自旋重试;acquireQueued 则负责自旋+park,直到成功或被中断。
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。
}
}
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,我深信...。
public final boolean release {
if ) { // 子类实现实际释放逻辑
Node h = head;
if
unparkSuccessor; // 唤醒后继非取消节点
return true;
}
return false;
}
The End? 当然不是!还有超时、中断响应、共享模式等一堆分支。下面随手丢几个片段,让你体会“一切皆模板方法”,划水。。
public final boolean tryAcquireNanos
throws InterruptedException {
if )
throw new InterruptedException;
return tryAcquire ||
doAcquireNanos;
}
...
private boolean doAcquireNanos
throws InterruptedException { ... }
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 大佬们请笑纳~*
#tryAcquire / #tryRelease / #tryAcquireShared / #tryReleaseShared 等核心点。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 官方文档,那里面可是有正式版解释哦~👋👋👋,无语了...
Demand feedback