简单聊下AQS

1.是什么

aqs即AbstractQueueSynchronizer。是并发大神doug lea的杰作。是java中一系列锁实现的基础,

比如常用的可重入锁ReentrantLock,CountDownLatch,Semaphore。

2.实现

状态state+CLH队列

根据当前state是否为0判断是否可以获取锁,队列是一个双向队列,其中每个结点有排队线程,结点的信号等信息。

以非公平锁为了,我们在外界调用lock方法的时候,源码是这样实现的

1):CAS尝试获取锁,获取成功则可以执行同步代码

2):CAS获取失败,则调用acquire方法,acquire方法实际上就是AQS的模板方法

3):acquire首先会调用子类的tryAcquire方法(又回到了ReentrantLock中)

4):tryAcquire方法实际上会判断当前的state是否等于0,等于0说明没有线程持有锁,则又尝试CAS直接获取锁

5):如果CAS获取成功,则可以执行同步代码

6):如果CAS获取失败,那判断当前线程是否就持有锁,如果是持有的锁,那更新state的值,获取得到锁(这里其实就是处理可重入的逻辑)

7):CAS失败&&非重入的情况,则回到acquire方法执行acquireQueued(addWaiter(Node.EXCUSIVE))「入队列」的操作,这里如果head和tail都为空,会先初始化一个空的node,作为哨兵结点,所以队列头节点始终是一个空值的node(信号量有值)

哨兵结点的作用?不用频繁判空,保持代码一致性。

8):将节点入队列之后,会判断「前驱节点」是不是头节点,如果是头结点又会用CAS尝试获取锁

9):如果是「前驱节点」是头节点并获取得到锁,则把当前节点设置为头结点,并且将前驱节点置空(实际上就是原有的头节点已经释放锁了)

10):没获取得到锁,则判断前驱节点的状态是否为SIGNAL,如果不是,则找到合法的前驱节点,并使用CAS将状态设置为SIGNAL

11):最后调用LockSupport.park(属于native本地方法)将当前线程挂起

<think> 我们正在讨论AQS(AbstractQueuedSynchronizer)的详细介绍。根据用户提供的引用,AQS是一个构建锁和同步器的框架,很多同步器如ReentrantLock、Semaphore、ReentrantReadWriteLock等都是基于AQS实现的。AQS的核心包括一个状态(state)和一个等待队列(CLH队列的变体)。下面将详细介绍AQS。 ### AQS的核心思想 AQS使用一个整型的`volatile`变量(state)来表示同步状态,通过内置的FIFO队列(CLH队列的变体)来完成资源获取线程的排队工作。 - **同步状态(state)**:通过`protected`类型的`getState()`, `setState()`, `compareAndSetState()`方法进行操作。不同的同步器对state的含义解释不同。例如: - 在`ReentrantLock`中,state表示锁的持有计数(0表示未锁定,大于0表示被锁定,并且可以重入)。 - 在`Semaphore`中,state表示当前可用的许可数。 - 在`CountDownLatch`中,state表示计数器的初始值,当计数减到0时,等待的线程被唤醒。 - **同步队列(等待队列)**:是一个双向链表,用于存储等待获取资源的线程。当线程尝试获取同步状态失败时,AQS会将当前线程以及等待状态等信息构造成一个节点(Node)并加入队列,同时阻塞当前线程。当同步状态释放时,会唤醒队列中的后继节点线程,使其再次尝试获取同步状态。 ### AQS的主要方法 AQS的设计是基于模板方法模式的,使用者需要继承AQS并重写指定的方法,然后将同步器组合在自定义同步组件的实现中,并调用AQS提供的模板方法。这些模板方法会调用使用者重写的方法。 需要重写的方法(protected方法): - `tryAcquire(int arg)`:独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行CAS设置同步状态。 - `tryRelease(int arg)`:独占式释放同步状态。 - `tryAcquireShared(int arg)`:共享式获取同步状态,返回负值表示失败;0表示成功,但没有剩余可用资源;正值表示成功,且有剩余资源。 - `tryReleaseShared(int arg)`:共享式释放同步状态。 - `isHeldExclusively()`:当前同步器是否在独占模式下被线程占用。 模板方法(public方法): - `acquire(int arg)`:独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则线程进入同步队列等待。该方法会调用重写的`tryAcquire(int arg)`方法。 - `acquireInterruptibly(int arg)`:与`acquire`类似,但响应中断。 - `tryAcquireNanos(int arg, long nanos)`:在`acquireInterruptibly`基础上增加了超时限制。 - `release(int arg)`:独占式释放同步状态,会唤醒同步队列中下一个节点。 - `acquireShared(int arg)`:共享式获取同步状态,同一时刻可以有多个线程获取到同步状态(如Semaphore)。 - `acquireSharedInterruptibly(int arg)`:共享式获取,响应中断。 - `tryAcquireSharedNanos(int arg, long nanos)`:共享式获取,响应中断且有超时。 - `releaseShared(int arg)`:共享式释放同步状态。 ### AQS的内部数据结构 AQS内部维护了一个双向链表(同步队列)和一个单向链表(条件队列,每个条件变量对应一个条件队列)。每个节点(Node)代表一个等待线程,节点中保存了线程的引用、等待状态(waitStatus)以及前驱和后继指针。 节点状态(waitStatus)包括: - `CANCELLED (1)`:节点被取消(例如由于超时或中断)。 - `SIGNAL (-1)`:后继节点需要被唤醒(当前节点的后继节点处于阻塞状态,当前节点释放锁或被取消时,需要唤醒后继节点)。 - `CONDITION (-2)`:节点在条件队列中等待。 - `PROPAGATE (-3)`:共享模式下,释放资源时需要传播给后续节点。 ### AQS的独占模式(以ReentrantLock为例) 在独占模式下,同一时刻只有一个线程能获取到同步状态。 1. **获取锁(acquire)**: - 线程调用`acquire`方法,内部调用`tryAcquire`(由子类实现)尝试获取锁。 - 如果成功,则线程继续执行。 - 如果失败,则构建节点(Node)并添加到同步队列尾部(通过CAS保证线程安全),然后调用`LockSupport.park()`阻塞当前线程。 2. **释放锁(release)**: - 线程调用`release`方法,内部调用`tryRelease`(由子类实现)释放锁(修改state)。 - 如果释放成功,则唤醒同步队列中的后继节点(如果存在)中的线程。 ### AQS的共享模式(以Semaphore为例) 在共享模式下,多个线程可以同时获取同步状态。 1. **获取共享资源(acquireShared)**: - 线程调用`acquireShared`方法,内部调用`tryAcquireShared`(由子类实现)尝试获取资源。 - 如果返回值大于等于0,表示获取成功,线程继续执行。 - 如果返回值小于0,表示获取失败,线程被加入同步队列并阻塞。 2. **释放共享资源(releaseShared)**: - 线程调用`releaseShared`方法,内部调用`tryReleaseShared`(由子类实现)释放资源(增加state)。 - 释放成功后,唤醒同步队列中后续的共享节点(可能有多个节点需要被唤醒,因为共享模式允许多个线程同时获取资源)。 ### 条件队列(Condition) AQS还提供了条件变量(Condition)的支持。每个条件变量对应一个条件队列。当线程调用`await()`方法时,线程会释放锁,并进入条件队列等待;当其他线程调用`signal()`方法时,会将条件队列中的一个线程转移到同步队列中,等待重新获取锁。 ### AQS的特性总结 - **可重入锁**:如ReentrantLock,支持同一线程多次获取锁。 - **公平与非公平锁**:AQS支持公平锁(按FIFO顺序获取锁)和非公平锁(允许插队,性能更高)。 - **共享与独占**:支持独占锁和共享锁两种模式。 - **可中断与超时**:支持可中断的获取锁以及超时获取锁。 ### 示例:自定义同步器 以下是一个简单的自定义同步器(独占模式),实现一个不可重入的锁: ```java class Mutex extends AbstractQueuedSynchronizer { // 尝试获取锁 protected boolean tryAcquire(int acquires) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } // 尝试释放锁 protected boolean tryRelease(int releases) { if (getState() == 0) throw new IllegalMonitorStateException(); setExclusiveOwnerThread(null); setState(0); return true; } // 是否独占 protected boolean isHeldExclusively() { return getState() == 1; } } ``` ### 总结 AQSJava并发包中同步组件的核心基础框架,它通过一个整型状态量和一个队列来管理同步状态和线程排队,提供了独占和共享两种模式,以及条件变量的支持。基于AQS可以方便地构建各种同步器[^1][^2][^3][^4]。 §§相关问题§§ 1. AQS中同步队列和条件队列有什么区别? 2. 如何基于AQS实现一个自定义的同步器? 3. AQS在实现公平锁和非公平锁时有什么不同? 4. AQS的state变量在不同同步器中的含义是什么? </think> ### AQS(AbstractQueuedSynchronizer)详细介绍 **AQSJava 并发包的核心基础框架**,用于构建锁和同步器。它通过一个 `volatile int state` 状态变量和内置的 FIFO 线程等待队列(CLH 队列变体)实现同步机制。JDK 中如 `ReentrantLock`、`Semaphore`、`CountDownLatch` 等同步器均基于 AQS 实现[^1][^2]。 --- #### 一、核心组件 1. **同步状态(state)** - 通过 `volatile int state` 表示共享资源状态 - 提供原子操作方法: ```java getState() // 获取当前状态 setState(int) // 直接设置状态 compareAndSetState(int expect, int update) // CAS 更新状态 ``` 2. **等待队列** - 双向链表结构的线程等待队列(CLH 变体) - 节点(Node)存储线程引用和等待状态(WAITING/CANCELLED) 3. **两种资源共享模式** - **独占模式(Exclusive)**:同一时刻只有一个线程可访问资源(如 `ReentrantLock`) - **共享模式(Shared)**:多个线程可同时访问资源(如 `Semaphore`) --- #### 二、工作流程(以独占模式为例) ```mermaid flowchart TD A[线程尝试获取锁] --> B{tryAcquire 成功?} B -->|是| C[执行同步代码] B -->|否| D[构造 Node 加入队列尾部] D --> E[自旋检查前驱节点] E -->|前驱是头节点| F{再次 tryAcquire} F -->|成功| G[设置自身为头节点] F -->|失败| H[阻塞线程] H -->|被唤醒| E C --> I[执行完毕调用 release] I --> J[tryRelease 释放状态] J --> K[唤醒后继节点] ``` 1. **获取资源** - 线程调用 `acquire(int arg)`: ```java if (!tryAcquire(arg)) addWaiter(Node.EXCLUSIVE) // 加入队列 acquireQueued(node, arg) // 阻塞或自旋 ``` 2. **释放资源** - 线程调用 `release(int arg)`: ```java if (tryRelease(arg)) unparkSuccessor(head) // 唤醒后继节点 ``` --- #### 三、关键设计特性 1. **模板方法模式** 开发者需重写以下方法实现自定义同步器: ```java protected boolean tryAcquire(int arg) // 独占式获取 protected boolean tryRelease(int arg) // 独占式释放 protected int tryAcquireShared(int arg) // 共享式获取 protected boolean tryReleaseShared(int arg) // 共享式释放 ``` 2. **公平性控制** - **公平锁**:严格按队列顺序获取资源(`ReentrantLock(true)`) - **非公平锁**:允许插队(默认模式,减少线程切换开销) 3. **可重入性** 同一线程可重复获取锁(通过 `state` 计数实现,如 `ReentrantLock`) 4. **条件队列支持** 通过 `ConditionObject` 实现等待/通知机制(类似 `Object.wait()`/`notify()`) --- #### 四、典型应用实现 1. **Semaphore(信号量)** - `state` 表示可用许可证数量 - 获取许可:`tryAcquireShared` 减少 `state`(若 `state<0` 则阻塞) - 释放许可:`tryReleaseShared` 增加 `state` 并唤醒阻塞线程[^3] 2. **ReentrantReadWriteLock(读写锁)** - 高并发读场景优化:共享模式处理读锁,独占模式处理写锁 - 状态拆分:高 16 位记录读锁数量,低 16 位记录写锁重入次数[^4] 3. **CountDownLatch** - `state` 初始化为计数阈值 - `countDown()`:调用 `tryReleaseShared` 递减 `state` - `await()`:当 `state=0` 时唤醒所有线程 --- #### 五、AQS 的优劣 ✅ **优势** - 减少并发编程复杂度(封装底层同步操作) - 高性能(避免重量级锁的 OS 内核态切换) - 扩展性强(可自定义同步策略) ❌ **局限** - 不适用于所有场景(如优先级调度) - 自旋等待可能增加 CPU 开销(需合理设置超时) > AQS 通过解耦 **资源访问** 与 **线程排队**,实现了同步器的通用化框架设计[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值