Java高频面试之并发编程-21
hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶
面试官:详细说说AQS
AQS(AbstractQueuedSynchronizer)是 Java 并发包(java.util.concurrent.locks)的核心基础框架,用于构建锁和其他同步器(如 ReentrantLock、Semaphore、CountDownLatch 等)。它通过模板方法模式定义了一套多线程访问共享资源的同步机制,开发者只需继承 AQS 并实现特定方法,即可自定义同步器。
AQS 的核心设计思想
AQS 的核心是 “同步状态管理” + “线程排队机制”:
-
同步状态(
state字段):
通过一个volatile int state字段表示共享资源的状态(如锁是否被占用、信号量剩余许可数等)。- 开发者需根据同步器的语义定义
state的用途(例如:ReentrantLock用state表示重入次数)。
- 开发者需根据同步器的语义定义
-
CLH 队列(线程等待队列):
采用 CLH 变体的双向链表队列,将未获取到资源的线程封装为Node节点并排队等待。- CLH 队列的每个节点保存了前驱和后继节点的引用,以及线程的状态(如是否被取消)。
-
模板方法模式:
AQS 提供通用的排队和阻塞机制,开发者只需实现以下关键方法(需保证线程安全):tryAcquire(int arg):尝试以独占模式获取资源。tryRelease(int arg):尝试释放独占模式的资源。tryAcquireShared(int arg):尝试以共享模式获取资源。tryReleaseShared(int arg):尝试释放共享模式的资源。isHeldExclusively():判断当前线程是否独占资源。
AQS 的内部结构
-
state字段private volatile int state; // 核心状态变量- 通过
getState(),setState(),compareAndSetState()方法原子操作状态。
- 通过
-
CLH 队列
// 队列头尾指针 private transient volatile Node head; private transient volatile Node tail;// Node 节点结构(每个等待线程封装为一个 Node) static final class Node {volatile int waitStatus; // 等待状态(如 CANCELLED、SIGNAL)volatile Node prev; // 前驱节点volatile Node next; // 后继节点volatile Thread thread; // 关联的线程Node nextWaiter; // 条件队列的下一节点(用于 Condition) } -
两种模式
- 独占模式(Exclusive):资源一次只能被一个线程占用(如
ReentrantLock)。 - 共享模式(Shared):资源可被多个线程共享(如
Semaphore、CountDownLatch)。
- 独占模式(Exclusive):资源一次只能被一个线程占用(如
AQS 的工作流程
1. 获取资源(以独占模式为例)
-
步骤:
- 调用
acquire(int arg)方法。 - 先尝试
tryAcquire(arg)(需开发者实现),若成功则直接返回。 - 若失败,将线程封装为
Node加入 CLH 队列尾部,并通过acquireQueued()自旋或阻塞等待。 - 在队列中等待的线程会不断检查前驱节点是否为头节点,若是则再次尝试获取资源。
- 调用
-
关键代码逻辑:
public final void acquire(int arg) {if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt(); // 恢复中断状态 }
2. 释放资源
-
步骤:
- 调用
release(int arg)方法。 - 先尝试
tryRelease(arg)(需开发者实现),若成功则唤醒队列中的后继节点。
- 调用
-
关键代码逻辑:
public final boolean release(int arg) {if (tryRelease(arg)) {Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h); // 唤醒后继节点的线程return true;}return false; }
3. 中断与超时
- AQS 支持可中断的获取资源(
acquireInterruptibly())和超时获取(tryAcquireNanos())。 - 若线程在等待中被中断或超时,会移除对应的节点并抛出
InterruptedException。
AQS 的应用场景
-
ReentrantLock- 通过实现
tryAcquire和tryRelease方法,管理锁的重入计数(state字段记录持有锁的线程和重入次数)。
- 通过实现
-
Semaphore- 使用
state表示剩余许可数,tryAcquireShared尝试获取许可,tryReleaseShared释放许可。
- 使用
-
CountDownLatchstate初始化为计数器值,await()调用acquireShared,countDown()调用releaseShared。
-
ReentrantReadWriteLock- 将
state的高 16 位用于读锁(共享模式),低 16 位用于写锁(独占模式)。
- 将
AQS 的设计优势
-
灵活性
开发者只需关注资源状态的管理(state操作),无需处理线程排队、阻塞与唤醒等底层逻辑。 -
高效性
- CLH 队列的变体设计减少了锁竞争,通过自旋和 CAS 操作提升性能。
- 双向链表便于处理取消和超时的节点。
-
可扩展性
支持独占和共享模式,且可通过Condition实现更复杂的线程通信(如生产者-消费者模型)。
AQS 的关键源码技巧
-
自旋 + CAS
AQS 大量使用 CAS(Unsafe类)和自旋操作,例如入队时通过 CAS 保证线程安全:private Node enq(final Node node) {for (;;) { // 自旋直到成功Node t = tail;if (t == null) { // 队列为空,初始化头节点if (compareAndSetHead(new Node()))tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}} } -
线程阻塞与唤醒
使用LockSupport.park()和LockSupport.unpark(thread)控制线程的阻塞与唤醒,避免优先级反转问题。
技术资料大全:https://pan.q删掉汉子uark.cn/s/aa7f2473c65b
总结
AQS 是 Java 并发包的基石,通过统一的框架解决了同步器的核心问题:
- 状态管理:通过
state字段灵活表示资源状态。 - 线程排队:CLH 队列高效管理等待线程。
- 模板方法:分离通用逻辑与具体实现,降低开发复杂度。
理解 AQS 是掌握 Java 并发编程的关键,但实际开发中建议优先使用 Java 内置的同步器(如 ReentrantLock),而非直接继承 AQS。

