当前位置: 首页 > ds >正文

AQS 抽象队列同步器 资源竞争-排队等待

目录

一、AQS的核心作用

二、AQS的核心组件

1. 同步状态(state)

2. 等待队列(CLH队列)

节点状态(waitStatus)的关键值:

三、AQS的两种工作模式

1. 独占模式(Exclusive Mode)

独占模式的获取流程(以acquire(1)为例):

独占模式的释放流程(以release(1)为例):

2. 共享模式(Shared Mode)

共享模式的获取流程(以acquireShared(1)为例):

共享模式的释放流程(以releaseShared(1)为例):

四、AQS的模板方法与钩子方法

1. 模板方法(AQS已实现,子类直接使用):

2. 钩子方法(子类按需实现,默认抛出异常):

五、基于AQS的典型同步工具

六、总结


AQS(AbstractQueuedSynchronizer,抽象队列同步器)是Java并发包(java.util.concurrent)的核心组件,是实现几乎所有同步工具(如ReentrantLockSemaphoreCountDownLatch等)的基础框架。它通过统一的模板方法和钩子方法,简化了同步器的开发,同时保证了高效的并发控制。

一、AQS的核心作用

AQS的本质是提供了一种“资源竞争-排队等待”的通用机制

  • 当线程竞争共享资源时,通过原子操作(CAS)尝试获取资源;
  • 若获取失败,线程会被包装成节点(Node),加入一个FIFO(先进先出)的等待队列中阻塞等待;
  • 当资源被释放时,队列中的线程会按顺序被唤醒,再次尝试获取资源。

简单说,AQS就像一个“交通指挥系统”:资源是“路口”,线程是“车辆”,AQS负责指挥车辆“抢道”和“排队”。

二、AQS的核心组件

AQS的功能依赖两个核心要素:同步状态(state)等待队列(CLH队列)

1. 同步状态(state)
  • 定义:AQS用一个volatile int state变量表示共享资源的状态(如“是否被占用”“剩余资源数量”等),其值的具体含义由子类(同步工具)定义。
  • 原子性保证:state的修改通过CAS操作(compareAndSetState)保证原子性,结合volatile的可见性,确保多线程对state的操作是线程安全的。
  • 示例
    • ReentrantLock中,state=0表示锁未被占用,state>0表示被占用(值为重入次数);
    • Semaphore中,state表示剩余的许可数量(可同时访问的线程数)。

2. 等待队列(CLH队列)

当线程获取资源失败时,会被封装成一个Node节点,加入到FIFO的等待队列中。队列的结构类似链表,每个节点包含以下核心信息:

  • Thread thread:当前等待的线程;
  • int waitStatus:节点状态(决定线程是否需要唤醒、是否已取消等);
  • Node prev/Node next:前驱和后继节点(构成双向链表)。

节点状态(waitStatus)的关键值:
  • CANCELLED(1):节点对应的线程已取消等待(如超时或被中断),会从队列中移除;
  • SIGNAL(-1):当前节点的后继节点需要被唤醒(当前节点释放资源后,需通知后继节点);
  • CONDITION(-2):节点处于条件队列中(用于Condition接口);
  • PROPAGATE(-3):共享模式下,资源释放会向后传播(唤醒多个节点);
  • 0:初始状态。

三、AQS的两种工作模式

AQS支持两种资源访问模式,子类可根据需求选择实现:独占模式共享模式

1. 独占模式(Exclusive Mode)
  • 特点:资源只能被一个线程独占(如锁),其他线程必须等待。
  • 核心操作
    • 获取资源acquire(int arg)(模板方法);
    • 释放资源release(int arg)(模板方法)。

独占模式的获取流程(以acquire(1)为例):
  1. 调用tryAcquire(1)(钩子方法,子类实现):尝试获取资源(如修改state)。
    • 若成功,直接返回(线程获取到资源);
    • 若失败,进入下一步。
  1. 失败的线程被包装成Node节点,通过addWaiter(Node.EXCLUSIVE)加入等待队列尾部。
  2. 调用acquireQueued(Node node, int arg):节点在队列中“自旋+阻塞”,直到被前驱节点唤醒后,再次尝试tryAcquire
    • 若期间线程被中断,会记录中断状态,待获取资源后再处理(不响应中断,除非用acquireInterruptibly)。

独占模式的释放流程(以release(1)为例):
  1. 调用tryRelease(1)(钩子方法,子类实现):尝试释放资源(如修改state)。
    • 若释放成功,进入下一步;
    • 若失败,直接返回。
  1. 唤醒等待队列中“最前面”的有效节点(通过unparkSuccessor方法,唤醒后继节点的线程)。

2. 共享模式(Shared Mode)
  • 特点:资源可被多个线程同时获取(如信号量、计数器)。
  • 核心操作
    • 获取资源acquireShared(int arg)(模板方法);
    • 释放资源releaseShared(int arg)(模板方法)。

共享模式的获取流程(以acquireShared(1)为例):
  1. 调用tryAcquireShared(1)(钩子方法,子类实现):尝试获取资源(返回值含义:>0表示成功且有剩余资源;0表示成功但无剩余;<0表示失败)。
    • 若返回值≥0,获取成功,直接返回;
    • 若返回值<0,进入下一步。
  1. 失败的线程被包装成Node节点,通过addWaiter(Node.SHARED)加入等待队列。
  2. 调用doAcquireShared(Node node):节点在队列中阻塞等待,被唤醒后再次尝试tryAcquireShared。若成功,会继续唤醒后续节点(因为共享模式允许多个线程获取)。

共享模式的释放流程(以releaseShared(1)为例):
  1. 调用tryReleaseShared(1)(钩子方法,子类实现):尝试释放资源(如增加state)。
    • 若释放成功,进入下一步;
    • 若失败,直接返回。
  1. 唤醒等待队列中的节点(可能唤醒多个,因为共享模式下多个线程可同时获取)。

四、AQS的模板方法与钩子方法

AQS通过模板方法定义了同步操作的整体流程(如获取/释放资源的步骤),并将具体的资源竞争逻辑(如何修改state)交给子类通过钩子方法实现。这种设计既保证了流程的统一性,又保留了灵活性。

1. 模板方法(AQS已实现,子类直接使用):
  • 独占模式:acquire()acquireInterruptibly()(响应中断)、tryAcquireNanos()(超时)、release()
  • 共享模式:acquireShared()acquireSharedInterruptibly()tryAcquireSharedNanos()releaseShared()

2. 钩子方法(子类按需实现,默认抛出异常):
  • 独占模式:
    • tryAcquire(int arg):尝试获取独占资源(返回true表示成功);
    • tryRelease(int arg):尝试释放独占资源(返回true表示成功)。
  • 共享模式:
    • tryAcquireShared(int arg):尝试获取共享资源(返回值≥0表示成功);
    • tryReleaseShared(int arg):尝试释放共享资源(返回true表示成功)。
  • 其他:isHeldExclusively()(判断当前线程是否独占资源,用于Condition)。

五、基于AQS的典型同步工具

AQS是Java并发工具的“基石”,以下是常见实现:

同步工具

模式

state含义

核心逻辑

ReentrantLock

独占模式

锁的重入次数(0=未占用)

tryAcquire:state=0时CAS抢占,state>0时判断是否当前线程(支持重入);tryRelease:减少state至0时释放。

Semaphore

共享模式

剩余许可数量

tryAcquireShared:减少许可(≥0成功);tryReleaseShared:增加许可。

CountDownLatch

共享模式

计数器值(初始为N)

tryAcquireShared:计数器=0时成功;tryReleaseShared:计数器减1(至0时唤醒所有等待线程)。

ReentrantReadWriteLock

混合模式

高16位=读锁计数,低16位=写锁重入次数

读锁(共享):增加读计数;写锁(独占):抢占并设置写计数。

六、总结

AQS通过“同步状态(state)+ 等待队列(CLH队列)”的核心设计,结合CAS原子操作和模板方法模式,为同步工具提供了高效、通用的实现框架。其核心价值在于:

  1. 统一了同步工具的竞争与等待逻辑,减少重复开发;
  2. 基于FIFO队列和CAS,保证了并发控制的高效性和公平性(可选);
  3. 支持独占和共享两种模式,覆盖了绝大多数同步场景。

理解AQS是掌握Java并发编程的关键,无论是使用现有工具还是自定义同步器,都需要深入理解其“资源竞争-排队唤醒”的底层逻辑。

http://www.xdnf.cn/news/16072.html

相关文章:

  • C++实战案例:从static成员到线程安全的单例模式
  • Django视图与路由系统
  • Elasticsearch、Solr 与 OpenSearch 搜索引擎方案对比分析及选型建议
  • 漏洞扫描 + 渗透测试:双轮驱动筑牢网络安全防线
  • 计算机发展史:个人计算机时代的多元融合与变革
  • cartographer内置评估工具使用流程:评估前端优化的误差
  • XSS学习总结
  • 【LeetCode数据结构】栈的应用——有效的括号问题详解
  • iOS 加固工具有哪些?快速发布团队的实战方案
  • Django Ninja
  • 【web 自动化】-6- 数据驱动DDT
  • AWS Certified Cloud Practitioner 认证考试 测试题与解析
  • CSS实现背景色下移10px
  • 自动化与安全 - 将 Terraform 集成到 CI/CD
  • rancher上使用rke在华为云多网卡的服务器上安装k8s集群问题处理了
  • 使用Trae简单编写一个登陆页面
  • 智能合约安全 - 重入攻击 - 常见漏洞(第一篇)
  • AUTOSAR进阶图解==>AUTOSAR_SWS_COMManager
  • 【JS逆向基础】数据库之MongoDB
  • c#转python第四天:生态系统与常用库
  • 近期工作感想:职业规划篇
  • Web开发 04
  • 【企业架构】TOGAF概念之一
  • Android系统5层架构
  • XSS知识总结
  • kafka生产端和消费端的僵尸实例以及解决办法
  • `MYSQL`、`MYSQL_RES` 和 `MYSQL_FIELD`的含义与使用案例
  • 【硬件】GalaxyTabPro10.1(SM-T520)刷机/TWRP/LineageOS14/安卓7升级全过程
  • 浅谈 Vue 的双向数据绑定
  • Java 字符集(Charset)详解:从编码基础到实战应用,彻底掌握字符处理核心机制