这是一个非常核心的Java并发编程面试题。我会用一个清晰易懂的方式为你全面解释AQS。
一、AQS 是什么?
AQS 的全称是 AbstractQueuedSynchronizer,即抽象队列同步器。
它是 Java 并发包(java.util.concurrent.locks)中一个构建锁和同步器的基础框架。你可以把它理解成一个“万能模板”,JDK中的许多重要的并发工具,其内部都是基于AQS来实现的。
核心思想:
AQS使用一个整型的volatile变量(命名为state)来表示同步状态(例如,锁被重入了多少次),并通过一个FIFO(先进先出)的线程等待队列,来处理那些未能获取到锁的线程的排队工作。
二、为什么需要 AQS?它解决了什么问题?
在AQS出现之前,如果想要构建一个锁或同步器,通常需要直接使用底层的 synchronized 关键字和 wait()/notify() 方法。这些底层机制存在一些问题:
-
难以控制:无法灵活地实现一些复杂的同步策略(比如,公平锁、尝试获取锁、带有超时的获取等)。
-
效率问题:
synchronized的锁升级过程在早期版本中性能开销较大。
AQS的出现,将构建同步器的通用逻辑(如线程排队、阻塞、唤醒)封装起来,开发者只需要关注一个核心问题:如何管理这个 state 状态变量。这大大简化了锁和同步器的构建。
三、AQS 的核心原理
AQS的核心原理可以概括为三大组件:
1. 状态变量(state)
-
这是一个用
volatile修饰的int变量。 -
它的值完全由使用者来决定其含义。
-
在 ReentrantLock 中,
state=0表示锁未被任何线程持有,state=1表示锁被一个线程持有,state>1表示锁被同一个线程重入了多次。 -
在 Semaphore 中,
state表示剩余的许可证数量。 -
在 CountDownLatch 中,
state表示需要倒数的计数。
-
2. 线程等待队列(CLH队列的变种)
-
这是一个双向链表,用于存放所有等待获取锁/资源的线程。
-
当线程尝试获取
state失败时,当前线程和相关的等待信息会被构造成一个节点(Node) 并加入到队列尾部。 -
队列的头节点(head)表示当前正持有锁的线程。
3. 获取/释放资源的模板方法
这是AQS作为“模板”的核心体现。它定义了两组核心方法:
-
获取资源(Acquire):如
acquire(int arg)-
尝试获取资源(由使用者实现)。
-
如果成功,则直接返回。
-
如果失败,则将线程加入等待队列,并可能阻塞线程。
-
-
释放资源(Release):如
release(int arg)-
尝试释放资源(由使用者实现)。
-
如果释放后状态允许唤醒后续线程,则唤醒队列中下一个等待的线程。
-
AQS已经实现了队列的入队、出队、阻塞、唤醒等复杂逻辑,但它把最关键的“如何尝试获取资源”和“如何尝试释放资源”留给了使用者去实现。
四、AQS 的关键方法
使用者需要继承 AbstractQueuedSynchronizer 并重写以下方法来实现自定义的同步逻辑:
| 方法名 | 描述 |
|---|---|
protected boolean tryAcquire(int arg) | 独占式尝试获取资源。成功返回true,失败返回false。 |
protected boolean tryRelease(int arg) | 独占式尝试释放资源。成功返回true,失败返回false。 |
protected int tryAcquireShared(int arg) | 共享式尝试获取资源。负数表示失败;0表示成功,但无剩余资源;正数表示成功,且有剩余资源。 |
protected boolean tryReleaseShared(int arg) | 共享式尝试释放资源。 |
protected boolean isHeldExclusively() | 当前同步器是否被独占模式占用。 |
模式说明:
-
独占模式(Exclusive):同一时刻只有一个线程能成功获取资源,如
ReentrantLock。 -
共享模式(Shared):同一时刻多个线程可以成功获取资源,如
Semaphore,CountDownLatch。
五、AQS 的应用实例
为了让你更直观地理解AQS的工作原理,下图展示了以 ReentrantLock 为例,其内部的AQS是如何协作的:
-
ReentrantLock:内部有一个
Sync类继承自 AQS。state=0表示锁空闲,state=1表示锁被占用。tryAcquire方法通过 CAS(Compare-And-Swap) 操作尝试将state从0改为1。 -
Semaphore:内部同样有一个
Sync类。state表示可用的许可证数量。acquire()就是消耗一个许可证(state--),release()就是增加一个许可证(state++)。 -
CountDownLatch:
state初始化为一个正数(计数)。await()方法会检查state是否为0;countDown()方法会将state减1,当state减到0时,唤醒所有等待的线程。
总结
-
AQS是什么? 它是一个构建锁和同步器的基础框架。
-
核心思想? 用一个
state变量表示状态,一个FIFO队列管理等待线程。 -
工作模式? 模板方法模式。AQS负责队列管理和线程调度,使用者只需实现
tryAcquire、tryRelease等核心状态控制方法。 -
为什么重要? 它是Java并发包的基石,理解了AQS,就理解了大部分JUC同步工具类的工作原理。
它是一个相对底层的组件,日常开发中直接使用的机会不多,但却是理解Java并发编程精髓的关键。
653

被折叠的 条评论
为什么被折叠?



