AQS 源码重走:从 Doug Lea 的 CLH 变体到 JDK 21 的完整路径
AQS 源码重走:从「如果让我写」到「原来还可以这样」 某开发者背了一周 AQS 八股:CLH 队列、acquireQueued、shouldParkAfterFailedAcquire……面试官问「AQS 的 prev 和 next 为什么一个可靠一个不可靠?」——答了,但说不出这么设计是为了解决什么问题。面试官一句话戳穿:「你读过源码,但你有没有想过在并发场景下删队列中的一个节点,不这么写会发生什么?」 背源码最大的问题是:你不知道那些代码是在解决什么问题。 所以这篇文章不走寻常路——先问「如果不用 AQS,让我自己写一个锁排队框架,该怎么下手?」然后一步步推演,当你发现「哎这里不安全怎么办」的时候,再引出 Doug Lea 的解法。这个过程会让人惊叹:原来并发代码还可以这样写。 最后,会专门对比 JDK 8 和 JDK 21 两个版本的 AQS,讲清楚 Doug Lea 为什么在 JDK 21 中对核心逻辑做了一次大重构。 🏗️ 从零开始:如果让我实现一个可排队的锁 假设只有 synchronized 和 LockSupport.park/unpark ,让你实现一个「锁没抢到就排队等」的框架,你打算怎么写? 第一次尝试:一个粗暴的实现 // 初版思路 class NaiveLock { volatile int state = 0; // 0=未锁, 1=已锁 Queue<Thread> queue = new ... // 等待队列 void lock() { while (!CAS(&state, 0, 1)) { // 没抢到锁 queue.enqueue(currentThread()); // 入队 park(); // 阻塞 } } void unlock() { state = 0; // 释放锁 Thread t = queue.dequeue(); // 从队列取一个 unpark(t); // 唤醒 } } 看上去好像没问题。但仔细想想,全是坑: ...

