AQS面试系列:只讲 AQS 三段源码,面试必问

18 阅读3分钟

aqs.png 说句大实话。 AQS 被写成一整套框架,是给 JVM 和 JDK 用的;
但在面试里,它被压缩成了三道判断题。

你能不能把这三段说清楚,
直接决定了面试官心里对你的定位:
“用过” vs “懂过”。


第一段:state —— 并发世界里最冷静的数字

很多人讲 AQS,会从队列开始。
但在我这儿,那是第二层

真正的起点,是这个看起来毫不起眼的字段:

public abstract class AbstractQueuedSynchronizer {
    private volatile int state;

    protected final int getState() {
        return state;
    }

    protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
}

面试时我通常会停在这儿,看你反应。

如果你开始讲“volatile 保证可见性”,
我会点头,但不会记分。

如果你接着说一句:

AQS 里没有“锁对象”,
谁改 state 成功,谁就暂时拥有秩序

那我基本知道,你翻过源码。

AQS 的所有公平、不公平、独占、共享,
最后都要落回到这个 state 上。
它不代表“线程”,只代表“资格”。

aqs2.png


第二段:acquire —— 不成功,就别硬撑

第二段,是我最爱问的。

“acquire 到底做了什么?”

大多数人会说:
“先 tryAcquire,失败就阻塞。”

这话没错,但不够真实

真实的 AQS acquire,长这样(简化版):

public final void acquire(int arg) {
    if (!tryAcquire(arg)) {
        Node node = addWaiter();
        acquireQueued(node, arg);
    }
}

这段代码的气质非常 AQS。

它没有 retry,没有 while 自旋到死,
只给你一次机会:

  • 成了,你走
  • 不成,进队

这一步非常关键,因为它直接回答了一个面试官心里一直在想的问题:

AQS 是“先排队再抢”,还是“先抢再排队”?

答案是:
先抢一次,失败才排队。

这也是为什么很多 AQS 锁默认不公平。
新线程是有“插队机会”的。


第三段:release —— 走之前,必须叫人

很多候选人写 AQS 子类的时候,
会特别认真地写 tryAcquire
然后在 tryRelease 上随手一写。

这是个非常危险的信号。

因为 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;
}

这段代码我面试时经常原样贴出来。

然后问一句很简单的问题:

“如果你 tryRelease 返回 true,但忘了唤醒,会怎样?”

真正懂 AQS 的人,通常会停顿一下,说:

后面的线程会永远睡着

这不是夸张,这是事实。

AQS 不会替你兜底。
你释放了资格,却没通知下一个人,
秩序就断在这儿。


把这三段连起来,AQS 就完整了

你会发现,其实整个 AQS 就是一条非常冷静的流水线:

  1. state 决定资格
  2. acquire 决定去留
  3. release 决定交接

队列只是载体,
公平与否只是策略,
共享和独占只是 state 的数学含义不同。

但这三段,是骨头。


面试里的“分水岭时刻”

如果你在面试中能自然地说出这些话:

  • “AQS 是先 CAS 抢一次,再决定排不排队”
  • “state 不是锁,是资格位”
  • “release 的重点不是 setState,是唤醒”

通常,面试官就不会再追源码细节了。

因为他已经知道一件事:
你不是只看过总结。