在2026年Java后端技术体系中,AI朗诵助手的底层并发调度与多线程任务处理,离不开JUC并发包的核心支柱——AbstractQueuedSynchronizer(AQS,抽象队列同步器)。AQS是所有锁和同步器的通用骨架,深入掌握它是理解Java并发编程的关键一步。
一、痛点切入:传统并发控制的局限性

在多线程环境下管理共享资源,常规方案依赖synchronized关键字:
public class Counter {private int count = 0; public synchronized void increment() { count++; } public synchronized int get() { return count; } }
synchronized存在三大痛点:
灵活性不足:无法尝试获取锁、无法超时获取、无法中断等待
功能单一:仅支持互斥锁,无法实现共享锁(如读锁)或信号量
扩展性差:难以定制自定义同步逻辑
这种“一刀切”的方案催生了AQS框架的诞生——开发者需要一个标准化、可扩展的底层支撑。
二、AQS核心概念
AQS全称AbstractQueuedSynchronizer(抽象队列同步器),它不是可直接使用的工具,而是一个构建锁和同步器的通用骨架-1。其核心思想是模板方法模式:将线程排队、阻塞、唤醒及同步状态管理等通用逻辑封装在框架中,具体的同步工具(如ReentrantLock、Semaphore)只需实现tryAcquire、tryRelease等钩子方法-1。
💡 生活化类比:AQS像“排队叫号系统”——state是“服务窗口”数量,CLH队列是“排队区”,CAS是“取号机”。AQS定义排队规则,具体业务窗口用不同语义解释state(如窗口可用数、倒计时等)。
AQS由两大核心组件构成:
1. 同步状态(state)
private volatile int state;state被volatile修饰保证线程可见性,具体含义由子类定义-1-10:
| 同步工具 | state含义 |
|---|---|
| ReentrantLock | 重入次数(0表示未加锁) |
| Semaphore | 剩余许可证数量 |
| CountDownLatch | 剩余倒计数 |
2. 等待队列(CLH队列变体)
AQS维护一个FIFO双向链表作为等待队列,存放竞争失败被阻塞的线程-1。队列节点(Node)包含:
prev/next:前后驱指针,构建双向链表waiter:封装的等待线程waitStatus:节点状态(CANCELLED=1被取消、SIGNAL=-1需唤醒后继、CONDITION=-2条件等待等)-1-4
三、CLH队列概念
CLH队列是Craig、Landin、Hagersten三人提出的队列锁方案-2。AQS在CLH基础上优化改良-2:
| 维度 | 经典CLH锁 | AQS变体 |
|---|---|---|
| 队列结构 | 单向队列,只指向前驱 | 双向链表,支持双向遍历 |
| 等待机制 | 自旋等待(消耗CPU) | park阻塞+唤醒 |
| 节点操作 | 仅尾部添加 | CAS原子入队+出队 |
AQS变体解决了经典CLH长时间自旋消耗CPU过高的问题-2。
四、概念关系:AQS与CLH
两者逻辑关系清晰:
AQS = 同步框架,CLH是其线程排队机制的具体实现
AQS定义完整同步流程,CLH只负责管理等待队列的数据结构
一句话记忆:AQS是骨架,CLH是骨架上“排队”这块骨头的实现细节
💡 面试金句:“AQS是同步框架,CLH是AQS中线程排队的底层数据结构。AQS使用CLH队列变体管理等待线程,用双向链表+CAS+park机制替代了经典CLH的自旋等待。”
五、代码示例:ReentrantLock如何基于AQS工作
以非公平锁的lock过程为例-19-15:
// 非公平锁的lock方法 final void lock() { if (compareAndSetState(0, 1)) // 直接CAS抢锁 setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); // 进入AQS排队逻辑 } // AQS的acquire方法 public final void acquire(int arg) { if (!tryAcquire(arg) && // 子类实现:再次尝试抢锁 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
执行流程拆解:
线程调用
lock(),先CAS尝试将state从0改为1,成功则获取锁CAS失败后调用
acquire(1)进入AQS排队addWaiter()将线程封装为Node加入CLH队列尾部acquireQueued()中,前驱是头节点的线程再次尝试获取锁,否则通过LockSupport.park()阻塞-15
新旧方案对比:传统synchronized由JVM内置实现、不可扩展、仅支持互斥;AQS框架通过模板方法+CLH队列+CAS,让子类只需定制state语义即可快速构建锁或同步器。
六、底层原理:volatile + CAS + LockSupport
AQS底层依赖三大技术支柱:
volatile:保证
state变量的内存可见性,一个线程的修改对其他线程立即可见-10CAS(Compare-And-Swap) :乐观锁机制,通过
Unsafe.compareAndSwapInt()实现原子化修改state,避免重量级锁开销-10LockSupport:
park()/unpark()实现线程精准阻塞与唤醒,比传统的wait/notify更灵活-4
AQS巧妙组合这三者,提供标准化并发同步骨架,上层只需关注业务语义。
七、高频面试题与参考答案
1. 谈谈你对AQS的理解(必问)
AQS全称AbstractQueuedSynchronizer,是JUC包的底层基石。核心有三部分:volatile int state表示同步状态,CLH队列管理等待线程,CAS操作原子更新state。它采用模板方法模式,定义了线程排队、阻塞、唤醒等通用逻辑,具体实现交给子类(如ReentrantLock重写tryAcquire/tryRelease)。
2. AQS独占模式和共享模式有什么区别?
独占模式(如ReentrantLock)同一时刻只有一个线程能持有锁,tryAcquire返回boolean,释放时只唤醒一个线程;共享模式(如CountDownLatch、Semaphore)允许多个线程同时访问,tryAcquireShared返回int表示剩余资源数,释放时可能唤醒多个线程-29。
3. CLH队列为什么要设计成双向链表?
主要三点:第一,线程阻塞前需检查前驱节点状态,双向链表可在O(1)时间找到前驱;第二,中断或超时的节点被标记CANCELLED需从队列移除,双向链表便于快速定位和移除异常节点;第三,实现公平锁时需判断当前节点的前驱是否为head,双向链表方便前驱查找-31。
4. 为什么说AQS使用了模板方法模式?
AQS作为抽象类,定义了acquire、release等骨架方法,把tryAcquire、tryRelease等留给子类实现-1。骨架方法定义了“先尝试获取,失败则排队阻塞”的整体流程,子类只需填空定义“如何获取/释放”,体现了模板方法模式“将算法骨架与具体实现分离”的精髓。
5. state变量的语义为什么这么灵活?
AQS不关心state的具体含义,只保证其原子更新和可见性。ReentrantLock用它表示重入次数,Semaphore表示剩余许可数,CountDownLatch表示倒计数,ReentrantReadWriteLock甚至把高16位存读锁、低16位存写锁。这种“最小化抽象、最大化扩展”的设计是AQS框架强大扩展性的根源-10。
八、结尾总结
本文围绕AQS核心概念、CLH队列实现机制以及两者关系展开,梳理了从痛点分析到原理示例再到面试要点的完整知识链路:
| 核心要点 | 关键结论 |
|---|---|
| AQS定位 | 同步框架(骨架) |
| CLH定位 | AQS中线程排队的底层数据结构 |
| 三大核心技术 | volatile + CAS + LockSupport |
| 两种同步模式 | 独占模式(ReentrantLock)、共享模式(CountDownLatch/Semaphore) |
| 面试高频考点 | 模板方法模式、CLH双向链表设计、state语义灵活性 |
💡 重点提醒:面试时回答AQS问题,务必兼顾宏观定位(同步框架)和微观细节(state、CLH队列、CAS),并说清独占/共享两种模式的区别。
下一篇将深入AQS源码级实现细节,分析acquireQueued中的自旋与阻塞博弈,以及ConditionObject条件队列的运作机制。关注AI朗诵助手技术专栏,持续获取硬核编程干货。
