AQS原理解析
作为一名常年被各种锁和同步问题“毒打”的Java程序员,深刻理解AQS原理绝对是面试通关必备技能。今天就来聊聊这个并发包核心基石,让你在面试时能清晰解析锁背后的运作机制。
AQS原理的核心,其实就是一个 state 变量 + 一个 FIFO等待队列(CLH变体) 。它定义了一套同步状态的管理和线程排队等待/唤醒的框架,像ReentrantLock、Semaphore、CountDownLatch这些工具都是基于它实现的。
核心机制:State与队列
想象面试官问:“AQS怎么管理同步状态的?”你可以这样答:
-
volatile int state: 这是核心!代表共享资源的状态。比如:- 在
ReentrantLock中,state=0表示锁空闲,state>0表示锁被持有(且数值代表重入次数)。 - 在
Semaphore中,state表示可用的许可证数量。 - 在
CountDownLatch中,state表示还需要等待的计数。 线程通过CAS操作(compareAndSetState)来尝试修改state,这是实现非阻塞同步的关键。
- 在
-
CLH队列(FIFO): 当线程尝试获取资源失败(比如
state不满足条件),AQS会将该线程包装成一个Node节点,通过CAS操作入队。这个队列是双向链表结构,遵循公平的FIFO原则(公平模式下)。
获取资源(acquire)流程
面试高频:“讲讲acquire(int arg)的过程?” 流程如下:
tryAcquire(需子类实现): 线程首先尝试直接获取资源。这是非阻塞操作,子类(如ReentrantLock.Sync)根据具体语义实现逻辑(通常是CAS修改state)。成功直接返回。- 入队等待: 如果
tryAcquire失败(资源已被占用),AQS调用addWaiter(Node.EXCLUSIVE)将当前线程包装成独占模式的Node节点,并通过CAS安全地添加到队尾。 acquireQueued: 入队后,在循环中:- 检查前驱节点是否是头节点(说明马上轮到自己了)。
- 如果是头节点,再次尝试
tryAcquire。 - 如果成功获取资源,将自己设为新的头节点(原头节点出队),返回。
- 如果不是头节点或再次失败,则调用
shouldParkAfterFailedAcquire判断是否需要阻塞当前线程(检查前驱节点的waitStatus,主要是看是否已设置SIGNAL)。 - 如果需要阻塞,则调用
parkAndCheckInterrupt方法,最终调用LockSupport.park(this)挂起线程,等待被唤醒。 - 唤醒后(通常是前驱节点释放资源后唤醒它),继续循环尝试步骤。
释放资源(release)流程
“那释放资源release(int arg)呢?”
tryRelease(需子类实现): 首先尝试释放资源。子类实现逻辑(通常是CAS修改state)。必须确保释放操作是线程安全的。- 唤醒后继: 如果
tryRelease成功(意味着资源可能变得可用),则调用unparkSuccessor(Node h)。- 找到头节点
h。 - 如果头节点的
waitStatus < 0(通常是SIGNAL),则通过CAS将其置零。 - 查找头节点之后第一个未取消的节点
s。 - 如果
s不为null,调用LockSupport.unpark(s.thread)唤醒该节点对应的线程。被唤醒的线程会从之前阻塞的地方(parkAndCheckInterrupt)继续执行,再次尝试获取资源(acquireQueued中的循环)。
- 找到头节点
共享模式 vs 独占模式
面试官可能追问:“AQS支持共享和独占两种模式,区别在哪?”
- 独占模式 (EXCLUSIVE): 资源一次只允许一个线程访问。如
ReentrantLock。上面描述的流程主要基于独占模式。 - 共享模式 (SHARED): 资源一次允许多个线程访问(数量由
state控制)。如Semaphore、CountDownLatch。- 获取 (
acquireShared): 基本流程类似独占模式acquire,但tryAcquireShared需要返回剩余可用资源数。成功获取后,如果还有剩余资源,会传播式唤醒后续的共享节点。 - 释放 (
releaseShared):tryReleaseShared成功后,需要传播式唤醒等待队列中的后继节点(共享节点)。
- 获取 (
AQS中的中断与超时
“AQS如何处理中断和超时?”
- 中断: 在
acquire方法族(acquireInterruptibly)或acquireQueued循环中检测到中断,会抛出InterruptedException。关键方法parkAndCheckInterrupt在唤醒后返回中断标志。 - 超时:
tryAcquireNanos等方法在获取资源时加入超时判断。如果在规定时间内未成功获取,则返回false。核心是使用LockSupport.parkNanos进行限时阻塞。
为什么理解AQS如此重要?
掌握AQS原理,就能看透大部分Java同步工具的内部运作。面试时被问到锁、信号量、栅栏等问题,都能从state、CLH队列、CAS、park/unpark这些底层机制切入,给出有深度的解析。这比死记硬背API定义强太多了!
福利时间: 想系统提升Java面试能力?这份 《2025年Java面试宝典》 绝对硬核! 🔵 链接: https://pan.baidu.com/s/1RUVf75gmDVsg8MQp4yRChg?pwd=9b3g 提取码: 9b3g
深度理解AQS原理,是应对并发面试难题的利器。如果你正准备冲刺大厂,需要系统刷题和资源,不妨留意一下。通过 面试鸭返利网 购买面试鸭会员,还能找我领取 25元返利!省下的钱买杯咖啡提神继续学,岂不美哉?祝大家面试顺利!



