一、说到线程同步,你会想到什么?
说起线程同步,很多人第一反应就是“锁”。
加锁、解锁,代码一围,问题好像就解决了。但你有没有认真想过一件事:那把锁,凭什么听你的?
如果把线程想成饭点冲进食堂的人,synchronized 更像一个门闩,谁先拽上算谁的;而 AQS 更像那个站在门口、不太爱说话、但规矩全写在脸上的管理员。他不解释、不安抚,也不跟你讲情绪,只做一件事:排队,按顺序来。
AbstractQueuedSynchronizer,简称 AQS,就是干这个的。
它让“同步”这件事不靠运气、不靠侥幸,而是靠规则。
所以作为面试官,我一般不会直接问你“懂不懂 AQS”。我更爱听你聊并发时,会不会自然地靠近它。
二、AQS 就是你面试里的突破口
一听到 AbstractQueuedSynchronizer 这个名字,很多人下意识就紧张了。
其实它一点都不神秘,它只是把并发里最麻烦、最容易写错的那部分事,统一收走了。
ReentrantLock、CountDownLatch、Semaphore,看起来是三种完全不同的工具,但你扒开外衣看本质,底下用的都是同一套 AQS 逻辑。
说白了,AQS 就干三件事:
用一个 state 表示“现在还能不能进”;抢不到的人,老实排队;走的时候,负责叫醒下一个。
你要是能把这三件事说顺,面试官基本已经在心里给你加分了。
三、别被 “Abstract” 这个词骗了
很多人第一次看 AQS,都会被 Abstract 这个词误导,觉得它就是个模板类,继承一下、填几个方法就完事。
但你真点进源码,会发现它一点都不“空”。
AQS 最核心的东西,甚至有点寒酸——一个 volatile int state。
public abstract class AbstractQueuedSynchronizer {
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
}
第一次看到这里,很多人都会愣一下:
就这?没有锁?没有 synchronized?
对,就这一个数字。
但它有个很狠的特点:只能 CAS 改。
能改成功的人,才有资格继续往下走。
四、tryAcquire 看着简单,其实全是“潜台词”
你自己写 AQS 子类时,几乎一定是从 tryAcquire 开始。
@Override
protected boolean tryAcquire(int arg) {
return compareAndSetState(0, arg);
}
写到这一步,很多人会松一口气:“哦,原来这么简单。”
但 AQS 真正的戏,从来不在这里,而在于:它怎么用你这个方法。
五、acquire 不是“加锁”,而是一整套流程
面试时我经常问一句:“acquire 到底干了什么?”
如果你只回答“调用 tryAcquire”,那就太浅了。
AQS 里真实发生的,是一整套非常冷静的流程:
public final void acquire(int arg) {
if (!tryAcquire(arg)) {
Node node = addWaiter();
acquireQueued(node, arg);
}
}
意思很直接:
你先试一次;不行,别站在门口了;进队列。
没有 retry,没有情绪。失败一次,立刻认清现实。
六、那个队列,是真正的“秩序来源”
AQS 的队列不是装饰品,它是整个同步体系真正的骨架。
每一个抢不到锁的线程,都会被包成一个 Node,老老实实挂到链表后面。
而且有个细节很多人不知道:AQS 默认不保证公平。
除非你自己写公平策略,否则它允许“刚来的线程”在合适的时机插队成功。
这也是为什么有些锁看起来反应很快,但并不绝对公平。
七、release 的重点,不在释放,而在“叫醒谁”
很多人以为 release 就是 state = 0,这是误会。
在 AQS 眼里,更重要的是:你走了以后,轮到谁?
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0) {
unparkSuccessor(h);
}
return true;
}
return false;
}
这段代码的气质很明显:
你能不能走,是你的事;但你要走,得负责叫人。
八、共享和独占,其实只是“放几个人进来”
独占模式很直观:门一次只开一个。
共享模式就现实多了:看 state 还剩多少。
tryAcquireShared 的返回值,本身就是并发控制里的“底层数学”:
小于 0,继续排;等于 0,你进但后面别来了;大于 0,你进,后面还能再进几个。
这不是业务语义,这是秩序。
九、真正容易翻车的地方,反而很安静
AQS 最大的坑,从来不是不会用,而是用得太自信。
忘了可重入;state 对了但唤醒错了;共享模式返回值理解反了。
AQS 不会提醒你,它默认你知道自己在干嘛。
也正因为这样,它看起来很冷,但在并发世界里异常可靠。
十、回到面试:为什么 AQS 能区分人?
因为聊 AQS 的人,很难装。
你可以背概念,但你骗不了 state、CAS、队列和唤醒。
真正懂 AQS 的人,说话时会自然地冒出这些词:
“先抢一次”“失败就进队”“release 要负责叫人”。
这些词一出来,面试官基本就知道:
你不是只看过博客。