AQS工作原理

AQS:AbstractQueuedSynchronizer,抽象队列同步器,用来构建锁和同步器。AQS相当于一个模版,而同步器则是AQS的具体应用。

AQS的发展

自旋锁:对一个变量执行CAS操作尝试获取锁。
——> 缺点:不能做到让权等待,可能出现饥饿问题,某个线程始终拿不到锁。

CLH锁:通过单向队列管理线程,而不是直接竞争共享变量。
——>缺点:大量自选操作浪费CPU资源。

AQS自旋+阻塞/唤醒,获取锁失败,先短暂自旋,如果还是失败,线程就进入阻塞状态。等待双向队列前面的线程释放锁,然后对后面的线程进行唤醒。

自定义同步器

  1. 自定义的同步器继承AbstractQueuedSynchronizer

  2. 重写AQS暴露的模版方法

    //独占方式。尝试获取资源,成功则返回true,失败则返回false。
    protected boolean tryAcquire(int)
    //独占方式。尝试释放资源,成功则返回true,失败则返回false。
    protected boolean tryRelease(int)
    //共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    protected int tryAcquireShared(int)
    //共享方式。尝试释放资源,成功则返回true,失败则返回false。
    protected boolean tryReleaseShared(int)
    //该线程是否正在独占资源。只有用到condition才需要去实现它。
    protected boolean isHeldExclusively()
    

资源共享方式

Exclusive独占:只有一个线程能执行

Share共享:多个线程可同时执行

队列Node的状态含义

状态含义
cancelled1线程取消获取锁。线程在等待获取资源时被中断或超时。
init0加入队列的新节点的初始状态。
signal-1后继节点需要当前节点唤醒
condition-2节点在等待Condition。当其他线程调用了Condition的signal()方法时,节点会从等待队列转移到同步队列获取锁
propagate-3前继节点不仅会唤醒后继节点,同时会唤醒后继节点的后继节点

图解AQS工作原理

因为AQS只是一个模版,没有具体实现,故这里以ReentrantLock为例。

前提:有3个线程尝试获取锁。分别为T1、T2、T3

  1. T1获取到锁,T2会进入到等待队列中等待获取锁。在进入队列之前,需要对队列进行初始化。

    在这里插入图片描述

  2. T2尝试获取锁。但锁被T1获取,T2进入等待队列。同时将head节点的状态0更新为signal,表示需要对head节点的后继节点进行唤醒。

在这里插入图片描述

  1. T3尝试获取锁。但锁被T1获取,T3进入等待队列。同时将T2的状态0更新为signal,表示需要对T2节点的后继节点T3进行唤醒。

    在这里插入图片描述

  2. 此时T1释放锁,唤醒后继节点T2,。T2被唤醒后,会退出等待队列,但不是真正的退出,而是将T2节点变成head节点。

    在这里插入图片描述

  3. T2释放锁,唤醒后继节点T3。T3被唤醒后,退出等待队列,但不是真正的退出,而是将T3节点变成head节点。

在这里插入图片描述

本文的图源来自javaGuide

<think> 嗯,用户再次询问AQS原理,看来ta对Java并发机制有深入研究需求。之前我已详细解释过AQS三大核心思想(模板方法、CLH队列、状态管理),但用户仍希望更系统化的原理说明。结合站内引用资料,这次需要突出四个维度:状态管理、队列运作、模板方法、公平性实现。 用户可能正在学习JUC包源码,或是面试准备。从ta连续追问AQS的行为看,理解深度要求较高。需特别注意CLH队列的变体特性——虽然引用[4]提到是双向FIFO队列,但原始CLH其实是单向自旋锁,AQS改进在于:1)双向链表便于取消节点时快速移除 2)使用LockSupport阻塞替代自旋 3)增加CONDITION状态支持条件变量。 状态管理方面要强调volatile state的CAS操作。引用[3]指出state决定资源空闲/占用状态,但未说明其灵活性——对ReentrantLock是重入计数,对Semaphore则是许可证数量。这点在回答中需举例说明。 模板方法部分要区分独占/共享模式差异。引用[4]给出的tryAcquire/tryRelease模板很清晰,但共享模式的tryAcquireShared返回int而非boolean的特性容易被忽略,需补充说明:正数表示剩余共享资源量,这是Semaphore能允许多线程同时获取的关键。 公平性实现其实藏在tryAcquire逻辑里。引用[5]的对比表显示公平性由“是否检查队列”决定,具体体现在:非公平锁直接CAS抢锁,公平锁则先检查hasQueuedPredecessors()。这个细节要展开,因为ReentrantLock默认非公平的性能优势正源于此。 最后需要点明LockSupport的作用。虽然各引用都提到线程阻塞/唤醒,但未说明其底层是Unsafe.park/unpark实现,这才是比wait/notify更高效的关键——精准唤醒特定线程且无monitor机制开销。 </think> AQS(AbstractQueuedSynchronizer)是 Java 并发包的核心框架,其原理基于 **状态管理**、**CLH 变体队列** 和 **模板方法模式**,主要流程如下: --- ### 一、核心组件 | **组件** | **作用** | |------------------|-------------------------------------------------------------------------| | **`volatile int state`** | 表示同步状态(如锁的重入次数、信号量剩余数量)[^3][^4] | | **CLH 变体队列** | 双向 FIFO 队列,存储等待线程(Node 节点)[^3][^4] | | **模板方法** | 子类重写 `tryAcquire`/`tryRelease` 等实现资源获取/释放逻辑[^1][^4] | --- ### 二、独占模式原理(以 ReentrantLock 为例) #### 1. **获取资源 (`acquire`)** ```java public final void acquire(int arg) { if (!tryAcquire(arg) && // 子类实现:尝试直接获取资源 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 失败则加入队列 selfInterrupt(); // 恢复中断状态 } ``` - **步骤**: 1. **尝试获取**:调用子类重写的 `tryAcquire`(例如通过 CAS 修改 `state`)。 2. **加入队列**:若失败,将线程包装为 **独占模式 Node**,通过 CAS 加入队列尾部(`addWaiter`)。 3. **阻塞等待**:在队列中自旋检查前驱节点是否为头节点,是则再次尝试获取;否则调用 `LockSupport.park()` 阻塞[^4][^5]。 #### 2. **释放资源 (`release`)** ```java public final boolean release(int arg) { if (tryRelease(arg)) { // 子类实现:尝试释放资源 Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); // 唤醒后继节点线程 return true; } return false; } ``` - **步骤**: 1. 子类重写 `tryRelease` 修改 `state`(如将重入次数减 1)。 2. 若 `state=0`(资源完全释放),唤醒队列中第一个有效节点线程(`unparkSuccessor`)[^4][^5]。 --- ### 三、共享模式原理(以 Semaphore 为例) #### 1. **获取资源 (`acquireShared`)** ```java public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) // 子类实现:返回剩余可用资源数 doAcquireShared(arg); // 资源不足时加入队列等待 } ``` - **与独占模式区别**: - `tryAcquireShared` 返回整数:**正数**表示剩余可用资源数,**负数**表示资源不足。 - 唤醒时**传播唤醒信号**(多个线程可同时获取资源)[^4]。 #### 2. **释放资源 (`releaseShared`)** ```java public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { // 子类实现:释放资源 doReleaseShared(); // 唤醒后续共享节点 return true; } return false; } ``` --- ### 四、公平性控制 | **模式** | **实现原理** | **示例** | |------------|-----------------------------------------------------------------------------|-----------------------| | **非公平锁** | `tryAcquire` 直接尝试抢锁(不检查队列) | `ReentrantLock(false)` | | **公平锁** | `tryAcquire` 先检查队列是否有等待线程(`hasQueuedPredecessors()`)才尝试获取[^5] | `ReentrantLock(true)` | --- ### 五、关键设计优化 1. **CLH 队列变体**: - 双向链表(原始 CLH 为单向),支持快速移除取消等待的节点。 - 节点状态 `waitStatus` 标记后继节点是否需要唤醒(如 `SIGNAL=-1`)[^4][^5]。 2. **避免惊群效应**: - 精准唤醒头节点的后继节点,而非唤醒所有线程[^4]。 3. **条件变量支持**: - 通过 `ConditionObject` 实现等待队列,与主同步队列协同[^4]。 --- ### 六、总结 AQS 的核心原理是: 1. **状态控制**:通过 `volatile state` 和 CAS 操作管理资源状态。 2. **队列管理**:CLH 队列处理竞争线程的阻塞与唤醒。 3. **模板方法**:子类定义资源获取/释放的具体逻辑(如 `ReentrantLock` 重入计数、`Semaphore` 许可证控制)[^1][^3][^4]。 > 示例:`ReentrantLock` 的 `state=0` 表示未锁定,`state=N` 表示被同一线程重入 N 次;线程竞争锁失败时加入队列,由前驱节点释放后唤醒。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值