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

同步工具的底层依赖:AQS

文章目录

目录

文章目录

前言

一、什么是AQS?

二、AQS原理

1.AQS框架

2.AQS原理

3.AQS数据结构

4.同步状态State

5.AQS 的核心流程(以独占模式为例)

1. 资源获取(acquire(int arg))

2. 资源释放(release(int arg))

三、AQS 在 JUC 中的典型应用

四、AQS 的核心优势

总结


前言

        在 JDK 1.5 之前,Java 的同步机制主要依赖synchronized关键字和Object类的wait()/notify()方法。这些机制虽然简单易用,但存在明显局限性;随着 Java 并发场景的复杂化(如多线程协作、高并发控制),需要更灵活、高效的同步工具。JDK 团队(以并发领域专家Doug Lea为核心)意识到:不同同步工具(锁、信号量、倒计时器等)的底层逻辑存在共性,可以抽象为一套通用框架。JDK 1.5 正式引入java.util.concurrent(JUC)包,标志着 Java 并发编程进入新阶段。AQS 作为 JUC 的 “基础设施”,被内置其中,成为几乎所有同步工具的底层依赖可以说,AQS 的出现让 JUC 包的同步工具实现变得 “模块化”。底层通用逻辑由 AQS 负责,上层具体规则由各工具自己定义(通过重写 AQS 的钩子方法)。这种设计极大降低了同步工具的开发难度,同时保证了性能和可靠性。

一、什么是AQS?

        在Java的核心类库中,Lock、Semaphore、ReentrantLock等线程同步类,都是基于AbstractQueuedSynchronizer(简称为AQS)实现。
        AQS是一种提供了原子式管理同步状态、阻塞和唤醒线程等功能,并提供等待队列模型的线程同步框架。

        AQS 解决了并发编程中最核心的问题:如何协调多个线程对共享资源的竞争。它通过封装通用的同步逻辑(如队列管理、线程阻塞),让开发者可以更简单地实现各种同步工具(无需重复处理复杂的线程交互细节)  例如,JUC 中的ReentrantLock(可重入锁)、Semaphore(信号量)、CountDownLatch(倒计时器)、CyclicBarrier(循环屏障)等,其底层都是基于 AQS 实现的。        

二、AQS原理

1.AQS框架

        总体来说,AQS框架共分为五层,自上而下由浅入深,从AQS对外暴露的API到底层基础数据。当有自定义同步器接入时,只需重写第一层所需要的部分方法即可,不需要关注底层具体的实现流程。当自定义同步器进行加锁或者解锁操作时,先经过第一层的API进入AQS内部方法,然后经过第二层进行锁的获取,接着对于获取锁失败的流程,进入第三层和第四层的等待队列处理,而这些处理方式均依赖于第五层的基础数据提供层。

2.AQS原理

        AQS核心思想是:如果被请求的共享资源空闲,那么就将当前请求资源的线程设置为有效的工作线程,将共享资源设置为锁定状态;如果共享资源被占用,就需要实现等待机制,来保证锁分配。 这种等待机制主要是基于CLH队列(一种虚拟双向队列)实现,将暂时获取不到锁的线程加入到队列中,实现队列等待。
        CLH是Craig、 Landin 、 Hagersten三个人发明的一种基于链表的队列,AQS中的队列是CLH变体,一种虚拟双向队列(FIFO),虚拟双向队列的意思是不存在队列实例,仅存在结点之间的关联关系。
        AQS通过将每条请求共享资源的线程,封装成CLH队列的一个节点来实现锁的分配。同时,AQS使用一个volatile修饰的int类型全局成员变量state来表示同步状态,在排队加锁的过程中,通过CAS完成对state值的修改。

3.AQS数据结构

AQS中最基本的数据结构Node:CLH变体队列中的节点

4.同步状态State

        AQS中维护了一个名为state的字段,意思是同步状态,使用volatile关键字修饰,用于展示当前共享资源的获锁情况。

//共享变量,使用volatile修饰保证线程可见性
private volatile int state;

方法名                                                                                                      描述
protected final int getState()                                                                    获取State的值
protected final void setState(int newState)                                             设置State的值
protected final boolean compareAndSetState(int expect, int update)    使用CAS方式更新State

        我们可以通过修改State字段表示的同步状态,实现多线程的独占模式和共享模式(加锁过程)。

独占模式:

                                   

共享模式:

                                      

5.AQS 的核心流程(以独占模式为例)

AQS 的工作流程围绕 “资源获取” 和 “资源释放” 展开,以下是独占模式下的完整流程:

1. 资源获取(acquire(int arg)

线程通过acquire(arg)尝试获取资源,流程如下:

public final void acquire(int arg) {// 1. 调用子类重写的tryAcquire()尝试获取资源if (!tryAcquire(arg) && // 2. 若失败,将线程封装为Node加入队列(enq()),并阻塞线程(acquireQueued())acquireQueued(enq(new Node(Thread.currentThread(), Node.EXCLUSIVE)), arg)) {// 3. 若线程在阻塞中被中断,记录中断状态selfInterrupt();}
}
  •  
    1. 尝试获取资源:调用tryAcquire(arg)(子类实现),若返回true(获取成功),直接返回;
    2. 入队操作:若获取失败,通过enq(Node)将线程封装为 Node 添加到队列尾部(FIFO);
      • enq()通过 CAS 循环确保节点被正确添加(处理并发入队);
    3. 阻塞等待acquireQueued(Node, arg)让节点中的线程进入阻塞状态,等待被唤醒;
      • 线程阻塞前会再次尝试获取资源(避免不必要的阻塞);
      • 若前驱节点是头节点且获取资源成功,则当前节点成为新头节点;
    4. 中断处理:若线程在阻塞中被中断,不会立即响应中断,而是记录中断状态(由上层决定是否处理)。
2. 资源释放(release(int arg)

持有资源的线程通过release(arg)释放资源,流程如下:

public final boolean release(int arg) {// 1. 调用子类重写的tryRelease()尝试释放资源if (tryRelease(arg)) {// 2. 释放成功,唤醒队列中的后继线程Node h = head;if (h != null && h.waitStatus != 0) {unparkSuccessor(h); // 唤醒后继节点}return true;}return false;
}
  1. 尝试释放资源:调用tryRelease(arg)(子类实现),若返回true(释放成功),继续;
  2. 唤醒后继线程:通过unparkSuccessor(Node)唤醒头节点的后继节点(SIGNAL状态的节点);
    • 唤醒前会将后继节点的状态重置为0
    • 被唤醒的线程会重新尝试获取资源(进入acquireQueued()的循环)。

三、AQS 在 JUC 中的典型应用

JUC 中的核心同步工具均基于 AQS 实现,以下是常见工具的底层逻辑:

工具类模式state含义核心逻辑
ReentrantLock独占重入次数(0 = 未锁定)tryAcquire实现公平 / 非公平锁竞争,tryRelease递减重入次数。
Semaphore共享剩余许可数tryAcquireShared减少许可,tryReleaseShared增加许可。
CountDownLatch共享倒计时剩余次数tryAcquireShared判断次数是否为 0,tryReleaseShared递减次数(到 0 唤醒所有线程)。
ReadWriteLock读写分离高 16 位 = 读计数,低 16 位 = 写计数读锁共享(tryAcquireShared),写锁独占(tryAcquire)。

四、AQS 的核心优势

  1. 通用性:通过模板方法封装共性逻辑,支持多种同步场景(独占 / 共享);
  2. 高效性:基于 CAS 和双向链表,避免锁竞争,性能优于synchronized(早期版本);
  3. 灵活性:支持超时获取、可中断获取、公平 / 非公平模式等高级特性;
  4. 可靠性:严格的 FIFO 队列保证线程竞争的公平性,避免饥饿问题。

总结

AQS 是 Java 并发编程的 “基石”,其核心思想是 **“状态管理 + 队列排队 + 模板方法”**。通过理解 AQS 的结构(state、CLH 队列)、工作模式(独占 / 共享)和核心流程(获取 / 释放资源),能深入掌握 JUC 同步工具的底层原理,甚至自定义符合业务需求的同步工具。AQS 的设计体现了 “抽象共性、隔离变化” 的软件设计思想,是并发编程领域 “高内聚、低耦合” 的典范。

好了,今天就到这里,今天依旧是深蹲不写BUG,我们一起加油努力!!!

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

相关文章:

  • Kubernetes 中为 ZenTao 的 Apache 服务器添加请求体大小限制
  • 如何开发一款高稳定、低延迟、功能全面的RTSP播放器?
  • 时序数据库选型指南:为何Apache IoTDB成为工业物联网首选
  • JVM分析(OOM、死锁、死循环)(JProfiler、arthas、jvm自带工具)
  • STM32 - Embedded IDE - GCC - 使用 GCC 链接脚本限制 Flash 区域
  • 【Android】从复用到重绘的控件定制化方式
  • HarmonyOS 应用开发深度解析:基于 ArkTS 的声明式 UI 与状态管理艺术
  • HarmonyOS安装以及遇到的问题
  • Jenkins-Ansible部署discuz论坛
  • 38.Ansible判断+实例
  • PINN物理信息神经网络用于求解二阶常微分方程(ODE)的边值问题,Matlab实现
  • 力扣hot100:缺失的第一个正数(哈希思想)(41)
  • Qwen3-30B-A3B 模型解析
  • 【C++】迭代器详解与失效机制
  • # Shell 文本处理三剑客:awk、sed 与常用小工具详解
  • 【前端面试题✨】Vue篇(一)
  • Linux网络序列化与反序列化(6)
  • Linux文本处理——awk
  • 飞牛OS Nas,SSH安装宝塔后,smb文件不能共享问题
  • STM32——串口
  • 2025年- H109-Lc1493. 删掉一个元素以后全为 1 的最长子数组(双指针)--Java版
  • 别再误会了!Redis 6.0 的多线程,和你想象的完全不一样
  • 从入门到实战:Linux sed命令全攻略,文本处理效率翻倍
  • 【机器学习深度学习】向量模型与重排序模型:RAG 的双引擎解析
  • 使用DataLoader加载本地数据 食物分类案例
  • GitHub Classroom:编程教育的高效协作方案
  • MySQL查询limit 0,100和limit 10000000,100有什么区别?
  • Shell编程从入门到实践:基础语法与正则表达式文本处理指南
  • 如何在部署模型前训练出完美的AI提示词
  • C# 中这几个主流的 ORM(对象关系映射器):Dapper、Entity Framework (EF) Core 和 EF 6