🍂 枫言枫语 :我是予枫,一名行走在 Java 后端与多模态 AI 交叉路口的研二学生。
"予一人以深耕,观万木之成枫。"
在这里,我记录从底层源码到算法前沿的每一次思考。希望能与你一起,在逻辑的丛林中寻找技术的微光。

在 Java 面试或者研读源码时,你一定会被问到:ReentrantLock 是怎么实现的?CountDownLatch 为什么能让线程等待?这些看似功能迥异的组件,其实背后都指向了同一个名字------AQS。
今天,我来带大家拆解这个 Doug Lea 大神留下的神作。
一、 AQS 到底是什么?
AQS ,全称 AbstractQueuedSynchronizer(抽象队列同步器)。
简单来说,它是一个框架。它把实现一个锁(或同步器)最繁琐的工作(比如线程排队、线程阻塞与唤醒、CAS 操作状态等)全部封装好了。你只需要在这个框架的基础上,填入一点点关于"如何获取锁"和"如何释放锁"的业务逻辑,就能造出一把功能完备的锁。
形象比喻:自动化的银行柜台
想象一个只有一个柜台的银行:
-
State(状态): 柜台是否有人(0 表示空闲,1 表示有人)。
-
CAS: 客户尝试冲向柜台的动作。
-
CLH 队列: 没抢到柜台的人,自觉去排队。
-
AQS 框架: 银行的自动排队机和叫号系统。它负责维护队列,并在柜台空闲时,叫醒下一个人。
二、 AQS 的"三大支柱"
要理解 AQS,必须死磕这三个核心组成部分:
1. 状态位:volatile int state
这是 AQS 的核心。所有的同步状态都靠它。
-
在
ReentrantLock中,state表示锁的持有次数(0 为空闲,1 为被占用,>1 为重入次数)。 -
在
Semaphore中,state表示剩余的可用许可数。 -
在
CountDownLatch中,state表示还需要等待的计数。
注意:
state是被volatile修饰的,保证了多线程下的可见性。
2. CAS (Compare And Swap)
AQS 频繁使用 CAS 指令来修改 state 的值。这是实现"无锁化"或"轻量级锁"的关键。只有 CAS 成功的线程,才算真正拿到了锁。
3. CLH 变体队列(双向链表)
当线程抢锁失败时,AQS 会将其封装成一个 Node 节点,并放入一个双向链表中。
-
头节点 (Head): 正在持有锁的那个线程。
-
后续节点: 正在苦苦等待被唤醒的线程。
三、 AQS 的两种工作模式
AQS 设计最精妙的地方在于它同时支持两种模式:
-
独占模式 (Exclusive):
一次只能有一个线程持有锁。如 ReentrantLock。
-
共享模式 (Shared):
允许多个线程同时获取。如 Semaphore(信号量)、ReadLock(读锁)、CountDownLatch。
这种"一套框架,两样通吃"的设计,体现了极高的抽象能力。
四、 源码级流程:一个线程的"抢锁之旅"
我们以 ReentrantLock.lock() 为例,看看 AQS 内部发生了什么:
第一步:acquire(1)
线程尝试获取锁。
-
调用
tryAcquire(1)。这个方法是由具体的子类(如NonfairSync)实现的。 -
如果成功(CAS 修改 state 从 0 变 1 成功),直接开干。
第二步:addWaiter(Node.EXCLUSIVE)
如果 tryAcquire 失败了,AQS 会把当前线程包装成一个 Node,塞进队列的尾部。这里用到了"自旋 + CAS"来保证线程安全地入队。
第三步:acquireQueued(node, 1)
这是最精彩的部分。线程入队后不会立即睡觉(挂起),而是会再看一眼:"我的前驱节点是不是头节点?"
-
如果是,说明快轮到我了,再试一次
tryAcquire。 -
如果不是,或者再次尝试也失败了,那就调用
LockSupport.park(this),心安理得地阻塞休眠。
五、 为什么 AQS 这么快?
-
自旋优化: 线程在进入阻塞状态前,会进行少量的自旋尝试,减少了内核态和用户态切换的开销。
-
双向链表: 便于取消某个排队节点(比如等待超时的线程),只需要修改前后指针即可。
-
模板方法模式: AQS 定义了骨架,复杂的线程调度逻辑由 AQS 处理,子类只需关心简单的状态修改逻辑。
六、 给"予枫"读者的总结
AQS 的精髓在于:它通过一个 volatile 状态位和一个高效的 FIFO 队列,完美地解决了多线程抢占资源的冲突问题。
如果你在准备面试,记住这几个关键词:volatile state 、CAS 、双向 CLH 队列 、LockSupport。
下一步建议
单纯看博文是不够的。建议你打开 IntelliJ IDEA,输入 ReentrantLock,顺着 lock() 方法点进去,看看那个叫 Sync 的内部类,你会发现它真的继承了 AbstractQueuedSynchronizer!
关于作者 : 💡 予枫 ,某高校在读研究生,专注于 Java 后端开发与多模态情感计算。💬 欢迎点赞、收藏、评论,你的反馈是我持续输出的最大动力!
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=9wrxwtlju1l
当前加入还有惊喜相送!