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

JUC——AQS

        AQS(AbstractQueuedSynchronizer,抽象队列同步器)​​ 是 Java 并发包(java.util.concurrent)中最核心、最底层也最重要的​​同步框架之一​​,它是构建锁和同步器(如 ReentrantLock、Semaphore、CountDownLatch、ReentrantReadWriteLock 等)的基石。


一、AQS 是什么?

​AQS(AbstractQueuedSynchronizer)​​ 是 Java 并发包中的一个​​抽象类​​,位于:

        java.util.concurrent.locks.AbstractQueuedSynchronizer

它为构建​​锁和同步器​​提供了一个​​灵活的、可重用的底层框架​​,通过它可以非常方便地实现:

  • 独占式获取与释放锁(如 ReentrantLock)​
  • ​共享式获取与释放资源(如 Semaphore、CountDownLatch、ReadWriteLock)​

🎯 ​​你可以把 AQS 理解为:构建锁和同步器的“骨架”或“基础设施”,它处理了线程排队、阻塞/唤醒、状态管理等复杂逻辑,开发者只需要专注实现“资源是否可被获取”的逻辑即可。​


二、AQS 的核心思想

AQS 的核心是围绕一个​​“状态”(state)变量​​,并通过一个​​FIFO 等待队列(CLH 队列的变种)​​来管理获取资源失败的线程。

核心组成:

组件

说明

​state(同步状态)​

一个 volatile int类型的变量,表示共享资源的状态,比如锁是否被占用、信号量剩余数量等。通过 CAS 操作保证原子性。

​CLH 队列(线程等待队列)​

一个 FIFO 的线程等待队列,用于存放那些​​获取资源失败的线程​​。是 AQS 实现线程排队和阻塞/唤醒的核心。

​模板方法模式​

AQS 采用​​模板方法设计模式​​,它定义了一系列​​protected 方法(如 tryAcquire、tryRelease)让子类去实现​​,而它自己实现了​​线程排队、阻塞、唤醒等通用逻辑​​。


三、AQS 的两种同步模式

AQS 支持两种资源访问模式:

1. ​​独占模式(Exclusive)​

  • 同一时刻​​只有一个线程​​能获取到资源(如 ReentrantLock)。
  • 方法:
    • tryAcquire(int):尝试获取资源,成功返回 true,失败返回 false。
    • tryRelease(int):尝试释放资源。
    • 对应的排队与阻塞机制由 AQS 内部实现。

 代表实现类:​​ReentrantLock​

2. ​​共享模式(Shared)​

  • 同一时刻​​可以有多个线程​​同时获取资源(如 Semaphore、CountDownLatch)。
  • 方法:
    • tryAcquireShared(int):尝试获取共享资源。
    • tryReleaseShared(int):尝试释放共享资源。
    • AQS 会控制并发线程数或资源数。

代表实现类:​​Semaphore、CountDownLatch、ReentrantReadWriteLock(读锁部分)​


四、AQS 的核心方法(分为两类)

        AQS 的设计采用了​​模板方法模式​​,它已经实现好了大部分通用逻辑(如线程排队、阻塞/唤醒),但关键的资源是否可获取/释放的逻辑,交由子类去实现。

1. ​​需要子类实现的方法(核心抽象/protected 方法)​

这些方法是 AQS 框架​​要求子类去实现​​的,用于定义​​“如何获取/释放资源”​​。

独占模式相关方法:

方法

说明

protected boolean tryAcquire(int arg)

尝试以独占方式获取资源,成功返回 true,否则 false

protected boolean tryRelease(int arg)

尝试以独占方式释放资源

protected boolean isHeldExclusively()

当前线程是否独占资源(用于判断是否是锁的持有者)

共享模式相关方法:

方法

说明

protected int tryAcquireShared(int arg)

尝试以共享方式获取资源,返回剩余资源数,负数表示失败

protected boolean tryReleaseShared(int arg)

尝试以共享方式释放资源

protected boolean isHeldExclusively()

是否被当前线程独占(某些共享锁也需要)

2. ​​AQS 已经实现的方法(通用逻辑,开发者一般不重写)​

这些方法由 AQS 自己实现,用于管理线程的排队、阻塞、唤醒等操作,包括:

  • acquire(int arg):独占式获取资源,如果获取不到就进入等待队列并可能阻塞。
  • release(int arg):独占式释放资源,并唤醒后续等待线程。
  • acquireShared(int arg):共享式获取资源。
  • releaseShared(int arg):共享式释放资源。
  • hasQueuedThreads():是否有线程在等待。
  • getQueueLength():获取等待队列长度。
  • ……

五、AQS 的内部实现原理(简化版)

1. ​​核心状态:state​

  • 是一个 volatile int变量,表示当前同步状态。
  • 比如:
    • 在 ​​ReentrantLock​​ 中,state = 0 表示锁未被占用,=1 表示被占用,>1 表示重入次数。
    • 在 ​​Semaphore​​ 中,state 表示剩余的许可数量。

2. ​​CLH 队列(线程等待队列)​

  • AQS 内部维护了一个 ​​FIFO 的线程等待队列​​,当某个线程获取资源失败时,会被封装为一个 ​​Node 节点加入队列尾部​​,并可能被挂起(阻塞)。
  • 当资源被释放时,AQS 会从队列头部唤醒一个或多个等待线程。

CLH 队列是 Craig、Landin 和 Hagersten 三人提出的一种​​基于链表的可扩展、高性能、公平的自旋锁队列​​,AQS 借鉴并优化了它的思想。

3. ​​Node 节点​

每个等待线程会被封装成一个 Node 对象,包含:

  • 线程引用(thread)
  • 等待状态(waitStatus)
  • 前驱/后继节点引用(prev / next)
  • 是独占模式还是共享模式

六、AQS 的使用:以 ReentrantLock 为例

        虽然我们一般不直接使用 AQS,但了解它有助于理解 Java 中各种锁和同步器的工作原理。

ReentrantLock 的底层就是基于 AQS 实现的独占锁:

  • ReentrantLock 内部有一个 ​​Sync 类(继承 AQS)​
  • Sync 又分为:
    • ​NonfairSync(非公平锁)​
    • FairSync(公平锁)​

ReentrantLock 通过调用 AQS 的:

  • acquire(1)→ 尝试获取锁,失败进入队列
  • release(1)→ 释放锁,并唤醒其他线程

而这些方法的内部逻辑,AQS 已经帮我们实现了,我们只需要通过继承 AQS 并实现 tryAcquire()和 tryRelease()方法,就能定制自己的锁!


七、如何自己基于 AQS 实现一个简单的同步器?

虽然很少直接使用 AQS,但理解它后,我们可以尝试实现一个​​简单的“自定义锁”或信号量​​。

简单示例:实现一个不可重入的独占锁(简化版)

class MyLock {private static class Sync extends AbstractQueuedSynchronizer {// 尝试获取锁@Overrideprotected boolean tryAcquire(int acquires) {if (compareAndSetState(0, 1)) { // state == 0 表示未被占用setExclusiveOwnerThread(Thread.currentThread()); // 设置当前线程为锁持有者return true;}return false;}// 尝试释放锁@Overrideprotected boolean tryRelease(int releases) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true;}// 是否被当前线程独占@Overrideprotected boolean isHeldExclusively() {return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();}}private final Sync sync = new Sync();public void lock() {sync.acquire(1);}public void unlock() {sync.release(1);}
}

八、总结:AQS 核心概念一句话

        ​​AQS(AbstractQueuedSynchronizer)是 Java 并发包中用于构建锁和同步器的核心框架,它通过一个 volatile 状态变量和 FIFO 等待队列,提供了线程同步的底层支持,开发者可通过继承 AQS 并实现 tryAcquire / tryRelease 等方法,轻松实现自定义的同步组件。​

参考:
并发——抽象队列同步器AQS的实现原理 - 特务依昂 - 博客园

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

相关文章:

  • 客流特征识别误报率↓76%!陌讯多模态时序融合算法在智慧零售的实战解析
  • 蓝凌EKP产品:从 XML 到 JSON ——表单存储的性能优化实践
  • [自用笔记]上传本地项目至github
  • 【嵌入式开发 Linux 常用命令系列 8 -- git checkout 解冲突详细介绍】
  • Qt工具栏中图标槽函数没有响应的问题分析
  • 十一、redis 入门 之 数据持久化
  • 基于FPGA的情绪感知系统设计方案:心理健康监测应用(一)
  • yggjs_rlayout框架v0.1.2使用教程 01快速开始
  • 基于RBF-GA的铝/镁异材FSLW工艺参数优化研究
  • Qt---架构文件.pro
  • 02-开发环境搭建与工具链
  • 鸿蒙中点击响应时延分析
  • 多核多线程应用程序开发可见性和乱序如何处理
  • css3之flex布局
  • Linux 学习笔记 - 集群管理篇
  • 音视频学习(五十五):H264中的profile和level
  • pyecharts可视化图表-scatter:从入门到精通
  • Trip Footprint旅行足迹App
  • jar包项目自启动设置ubuntu
  • Vue中 this.$emit() 方法详解, 帮助子组件向父组件传递事件
  • Altium Designer 22使用笔记(9)---PCB布局、布线操作
  • 复杂街景误检率↓79%!陌讯时空建模算法在非机动车乱停放检测的实战解析
  • 点播视频预览是怎么做到的?
  • VsCode使用SFTP连接Linux
  • 使用 Golang 的 Gin 框架实现一周极限编程计划:全网 AIGC 项目热点追踪应用
  • MATLAB 与 Simulink 联合仿真:控制系统建模与动态性能优化
  • yggjs_rlayout框架v0.1.2使用教程 02 TechLayout 布局组件
  • 上科大解锁城市建模新视角!AerialGo:从航拍视角到地面漫步的3D城市重建
  • nginx部署goaccess监控
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day12