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

AQS原理解析

1. 什么是AQS


AQS的全称是AbstractQueuedSynchronizer,即抽象队列同步器,这个类在java.uitl.concurrent.locks包下面。

AQS就是一个抽象类,主要用来构建锁和同步器

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
}

AQS为构建锁和同步器提供了一些通用功能的实现,因此使用AQS能简单且高效地构建出应用广泛的大量的同步器
比如常用的ReentrantLockSemaphore,其他类似ReentrantReadWriteLockSynchronousQueue等等皆是基于 AQS 的

2. AQS原理


2.1 AQS核心思想


AQS核心思想在于:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。
如果被请求的共享资源被占用,那么就需要一套阻塞等待以及被唤醒时锁分配的机制,这个机制是基于CLH锁实现的。

CLH锁是对自旋锁的一种改进,是一个虚拟的双向队列(虚拟的双向队列不存在队列实例,仅存在结点之间的关系),暂时获取不到锁的线程将被加入到队列中。AQS将每条请求共享资源的线程封装为一个CLH队列锁的一个结点(Node)来实现锁的分配。在CLH队列锁中,一个节点表示一个线程,它保存着线程的引用、当前节点在队列中状态、前驱节点、后继节点。

Node结点的源码如下所示:

abstract static class Node {  volatile Node prev;       // 前驱结点volatile Node next;       // 后继结点 Thread waiter;            // 线程引用volatile int status;      // 线程的状态// methods for atomic operations    final boolean casPrev(Node c, Node v) {  // for cleanQueue  return U.weakCompareAndSetReference(this, PREV, c, v);  }  final boolean casNext(Node c, Node v) {  // for cleanQueue  return U.weakCompareAndSetReference(this, NEXT, c, v);  }  final int getAndUnsetStatus(int v) {     // for signalling  return U.getAndBitwiseAndInt(this, STATUS, ~v);  }  final void setPrevRelaxed(Node p) {      // for off-queue assignment  U.putReference(this, PREV, p);  }  final void setStatusRelaxed(int s) {     // for off-queue assignment  U.putInt(this, STATUS, s);  }  final void clearStatus() {               // for reducing unneeded signals  U.putIntOpaque(this, STATUS, 0);  }  private static final long STATUS  = U.objectFieldOffset(Node.class, "status");  private static final long NEXT    = U.objectFieldOffset(Node.class, "next");  private static final long PREV    = U.objectFieldOffset(Node.class, "prev");  
}

CLH队列结构如下图所示:
![[Pasted image 20240819145542.png]]

AQS(AbstractQueuedSynchronizer)的核心原理图:
![[Pasted image 20240819145559.png]]

AQS使用成员变量state,表示同步状态,通过内置的FIFO线程等待/等待队列来完成获取资源线程的排队工作。
AQS抽象类中内置了head和tail结点。

/**  * Head of the wait queue, lazily initialized. */private transient volatile Node head;  /**  * Tail of the wait queue. After initialization, modified only via casTail. */private transient volatile Node tail;

state变量由volatile修饰,用于展示当前临界资源的获取锁的情况。

private volatile int state;

另外,状态信息state可以通过protected类型的getState()setState()compareAndSetState()进行操作。并且,这几个方法都是通过final修饰的,在子类无法被重写。

//返回同步状态的当前值
protected final int getState() {return state;
}// 设置同步状态的值
protected final void setState(int newState) {state = newState;
}
//原子地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)
protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

以可重入的互斥锁ReentrantLock为例,它的内部维护了一个state变量,用来表示锁的占用状态

  1. state的初始值为0,表示锁处于未锁定状态
  2. 当线程A调用了lock()方法时,会尝试通过tryAcquire()方法独占锁并让state的值+1。
  3. 如果成功了,那么线程A就获取到了锁,如果失败了,那么线程A就会被加入到一个等待队列中(CLH队列)直到其他线程释放该锁。
  4. 假设线程A获取锁成功了,释放锁之前,A线程自己是可以重复获取此锁的(state会累加)。
  5. 这是可重入的表现:一个线程可以多次获取同一个锁而不会被阻塞,但是,这也意味着,一个线程必须释放全部次数的锁,
    才可以让state变为0,就是让锁恢复到未锁定的状态。只有这样,其他等待的线程才可以去获取锁。

线程 A 尝试获取锁的过程如下图所示:
![[Pasted image 20240819151108.png]]

倒计时器CountDownLatch为例子:

  1. 任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。
  2. 这N个子线程开始执行任务,每执行完一个子线程,就调用一次countDown()方法。
  3. 该方法会尝试使用CAS操作,让state的值减少1。
  4. 当所有的子线程执行完毕后,CountDownLatch会调用unpark()方法,唤醒主线程。
  5. 这时主线程就可以从await()方法(CountDownLatch中的await()方法而非AQS中的)返回,继续执行后续的操作。

2.2 AQS资源共享方式


AQS定义两种资源共享方式:Exclusive(独占,只有一个线程能执行,如ReentrantLock)和Share(共享,多个线程可同时执行,如Semaphore和CountDownLatch)

一般来说,自定义同步器的共享方式要么是独占,要么是共享,他们只需要实现tryAcquire-tryReleasetryAcquireShared-tryReleaseShared中的一种即可。但 AQS 也支持自定义同步器同时实现独占和共享两种方式,如ReentrantReadWriteLock

2.3 自定义同步器


同步器的设计是基于模板方法模式的,如果需要自定义同步器,一般的方式如下:

  1. 使用者继承AbstractQueuedSynchronizer 并重写指定的方法。
  2. 将 AQS 组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。

AQS使用了模板方法模式,自定义同步器时需要重写下面的几个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()

相关文章:

AQS原理解析

1. 什么是AQS AQS的全称是AbstractQueuedSynchronizer,即抽象队列同步器,这个类在java.uitl.concurrent.locks包下面。 AQS就是一个抽象类,主要用来构建锁和同步器。 public abstract class AbstractQueuedSynchronizer extends AbstractOw…...

『 Linux 』利用UDP套接字实现简单群聊

文章目录 服务端通过传入命令处理实现远程命令执行使用Windows编辑UDP客户端实现Windows远程控制Linux接收套接字的其他信息UDP套接字简单群聊服务端UDP套接字简单群聊客户端运行测试及分离输入输出 参考代码 服务端通过传入命令处理实现远程命令执行 『 Linux 』利用UDP套接字…...

【数据结构与算法 | 图篇】最小生成树之Kruskal(克鲁斯卡尔)算法

1. 前言 克鲁斯卡尔算法(Kruskals algorithm)是一种用于寻找加权图的最小生成树(Minimum Spanning Tree, MST)的经典算法。这种算法是由约瑟夫克鲁斯卡尔(Joseph Kruskal)提出的,并且适用于所有…...

了解常用的代码检查工具

在软件开发领域,代码检查工具是确保代码质量、提高开发效率、促进团队协作的重要工具。这些工具通过自动化分析代码,帮助开发者发现潜在的错误、漏洞、代码异味等问题,并提供修复建议或重构方案。以下是一些常用的代码检查工具,它…...

BUUCTF PWN wp--warmup_csaw_2016

第一步 先checksec一下(没有启用NX保护、PIE、完整的RELRO和栈保护,还有具有RWX权限的内存段。) 分析一下这个文件的保护机制: Arch: amd64-64-little 这表示该可执行文件是为64位的AMD64架构编译的,并且使用的是小…...

dockerfile搭建部署LNMP

目录 实验 架构: 实验步骤: nginx部分 mysql部分 php部分 实验 实验:用dockerfile搭建LNMP论坛 架构: 一台docker虚拟机 docker部署nginx 1.22 指定ip地址172.111.0.10 docker部署mysql 8.0.30 指定ip地址…...

Rust : 数据分析利器polars用法

Polars虽牛刀小试,就显博大精深,在数据分析上,未来有重要一席。 下面主要列举一些常见用法。 一、toml 需要说明的是,在Rust中,不少的功能都需要对应features引入设置,这些需要特别注意,否则编译…...

Qt第一课

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…...

论“graphics.h”库,easyx

前言 别人十步我则百,别人百步我则千 你是否有这样的想法,把图片到入进c里,亦或者能实时根据你发出的信息而做出回应的程序,graphics.h这个库完美满足了你的需求,那今天作者就给大家介绍一下这个库,并做一些…...

如何在寂静中用电脑找回失踪的手机?远程控制了解一下

经过一番努力,我终于成功地将孩子哄睡了。夜深人静,好不容易有了一点自己的时间,就想刷手机放松放松,顺便看看有没有重要信息。但刚才专心哄孩子去了,一时就忘记哄孩子之前,顺手把手机放哪里去了。 但找过手…...

Android 实现动态换行显示的 TextView 列表

在开发 Android 应用程序时,我们经常需要在标题栏中显示多个 TextView,而这些 TextView 的内容长度可能不一致。如果一行内容过长,我们希望它们能自动换行;如果一行占不满屏幕宽度,则保持在一行内。本文将带我们一步步…...

Golang | Leetcode Golang题解之第352题将数据流变为多个不相交区间

题目&#xff1a; 题解&#xff1a; type SummaryRanges struct {*redblacktree.Tree }func Constructor() SummaryRanges {return SummaryRanges{redblacktree.NewWithIntComparator()} }func (ranges *SummaryRanges) AddNum(val int) {// 找到 l0 最大的且满足 l0 < val…...

Ubuntu安装mysql 以及远程连接mysql Windows—适合初学者的讲解(详细)

目录 准备工作 一.Xshell中操作 &#xff08;1&#xff09;在虚拟机中安装mysql &#xff08;2&#xff09;连接Windows数据库 &#xff08;3&#xff09;进入linux数据库。 &#xff08;4&#xff09;修改mysql配置文件 二.Windows命令窗口操作 需要软件虚拟机&#xff0c;Xsh…...

【数学建模】MATLAB快速入门

文章目录 1. MATLAB界面与基本操作1.1 MATLAB的基本操作 2. MATLAB字符串和文本2.1 string变量2.2 char变量 3. MATLAB的矩阵运算 1. MATLAB界面与基本操作 初始界面&#xff1a; 刚开始的界面只要一个命令行窗口&#xff0c;为了使编辑界面出现我们需要新建一个文件&#xff…...

【ubuntu24.04】k8s 部署5:配置calico 镜像拉取

kubeadm - 中国大陆版建议:初始化Kubeadm –apiserver-advertise-address 这个地址是本地用于和其他节点通信的IP地址 –pod-network-cidr pod network 地址空间 sudo kubeadm init --image-repository registry.aliyuncs.com/google_containers --apiserver-advertise-add…...

Elasticsearch 的数据备份与恢复

在生产环境中&#xff0c;数据的安全性和可靠性至关重要。对于基于 Elasticsearch 的系统而言&#xff0c;数据备份与恢复是确保数据完整性、应对灾难恢复的关键操作。本文将详细介绍 Elasticsearch 中如何进行数据备份与恢复&#xff0c;帮助管理员构建一个可靠的数据保护策略…...

Ps:首选项 - 暂存盘

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“暂存盘” Scratch Disks选项卡通过合理配置和管理暂存盘&#xff0c;可以显著提高 Photoshop 的运行性能&#xff0c;特别是在处理复杂的设计项目或大型图像文件时。选择合适…...

力扣217题详解:存在重复元素的多种解法与复杂度分析

在本篇文章中&#xff0c;我们将详细解读力扣第217题“存在重复元素”。通过学习本篇文章&#xff0c;读者将掌握如何使用多种方法来解决这一问题&#xff0c;并了解相关的复杂度分析和模拟面试问答。每种方法都将配以详细的解释&#xff0c;以便于理解。 问题描述 力扣第217…...

享元模式:轻量级对象共享,高效利用内存

享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;用于减少对象数量、降低内存消耗和提高系统性能。它通过共享相似对象的内部状态&#xff0c;减少重复创建的对象。下面将具体介绍享元模式的各个方面&#xff1a; 组成 抽象享元&#xff0…...

人工智能-自然语言处理(NLP)

人工智能-自然语言处理&#xff08;NLP&#xff09; 1. NLP的基础理论1.1 语言模型&#xff08;Language Models&#xff09;1.1.1 N-gram模型1.1.2 词嵌入&#xff08;Word Embeddings&#xff09;1.1.2.1 词袋模型&#xff08;Bag of Words, BoW&#xff09;1.1.2.2 TF-IDF&a…...

Unity Shader UV 坐标与纹理平铺Tiling Offset 深度解析

从 UV 空间的数学本质出发&#xff0c;理解 URP 中纹理坐标的缩放&#xff08;Tiling&#xff09;与偏移&#xff08;Offset&#xff09;控制原理&#xff0c; 并掌握 Shader Graph、HLSL、C# 三种维度的实践技巧。UV 坐标系基础在实时渲染中&#xff0c;UV 坐标是将二维纹理贴…...

C语言开发者视角:Kandinsky-5.0-I2V-Lite-5s高性能推理引擎调用

C语言开发者视角&#xff1a;Kandinsky-5.0-I2V-Lite-5s高性能推理引擎调用 1. 引言&#xff1a;当静态告警遇上动态生成 想象一下这样的场景&#xff1a;工业监控系统捕捉到设备异常&#xff0c;触发静态告警图片。传统方案中&#xff0c;这张图片需要人工介入分析&#xff…...

2026年正点原子开发板移植方案——从0开始的Rootfs之路(5)WSL + NFS 网络启动踩坑记:从挂载失败到成功启动的完整历程

2026年正点原子开发板移植方案——从0开始的Rootfs之路&#xff08;5&#xff09;WSL NFS 网络启动踩坑记&#xff1a;从挂载失败到成功启动的完整历程项目已经开源&#xff01;尝试使用IMX-Forge给你的开发板跑新的Linux 7.0内核&#xff1a;https://github.com/Awesome-Embe…...

Pixel Epic动态卷轴效果展示:从空白屏幕到完整研报的实时生成录屏

Pixel Epic动态卷轴效果展示&#xff1a;从空白屏幕到完整研报的实时生成录屏 1. 引言&#xff1a;当科研遇上像素冒险 在传统的研究报告撰写过程中&#xff0c;我们常常面对冰冷的界面和机械化的交互体验。Pixel Epic彻底改变了这一现状&#xff0c;将严肃的学术研究变成了一…...

零基础如何用罗技鼠标宏实现绝地求生自动压枪?高效配置指南

零基础如何用罗技鼠标宏实现绝地求生自动压枪&#xff1f;高效配置指南 【免费下载链接】logitech-pubg PUBG no recoil script for Logitech gaming mouse / 绝地求生 罗技 鼠标宏 项目地址: https://gitcode.com/gh_mirrors/lo/logitech-pubg 你是否在《绝地求生》中因…...

解锁开源工具QMK Toolbox:完全掌握机械键盘个性化定制

解锁开源工具QMK Toolbox&#xff1a;完全掌握机械键盘个性化定制 【免费下载链接】qmk_toolbox A Toolbox companion for QMK Firmware 项目地址: https://gitcode.com/gh_mirrors/qm/qmk_toolbox QMK Toolbox是一款开源的设备管理工具&#xff0c;专为QMK固件设计&…...

EasyAnimateV5-7b-zh-InP效果对比:不同Sampling Method(Flow/Euler)画质差异

EasyAnimateV5-7b-zh-InP效果对比&#xff1a;不同Sampling Method&#xff08;Flow/Euler&#xff09;画质差异 你是不是也遇到过这样的困惑&#xff1a;用同一个图生视频模型&#xff0c;同样的图片和提示词&#xff0c;只是换了个采样方法&#xff0c;出来的视频效果就天差…...

轻量化之路:使用模型剪枝与量化技术压缩卡证检测模型

轻量化之路&#xff1a;使用模型剪枝与量化技术压缩卡证检测模型 1. 引言 你有没有遇到过这样的场景&#xff1f;想把一个识别身份证、银行卡的AI模型塞进手机App里&#xff0c;或者部署到一台小小的工控机上&#xff0c;结果发现模型动辄几百兆&#xff0c;跑起来慢吞吞&…...

忍者像素绘卷:天界画坊Python入门实战,3步搭建AI绘画环境

忍者像素绘卷&#xff1a;天界画坊Python入门实战&#xff0c;3步搭建AI绘画环境 1. 前言&#xff1a;当Python遇见像素艺术 还记得小时候玩过的8-bit游戏吗&#xff1f;那些由一个个小方块组成的像素世界&#xff0c;如今正以全新的方式回归。天界画坊是一个开源的AI绘画工具…...

FCOS3D vs PGD:单目3D检测两大算法核心差异与选型指南

FCOS3D与PGD&#xff1a;单目3D检测技术深度对比与工程实践指南 1. 技术背景与核心挑战 在自动驾驶和机器人感知领域&#xff0c;单目3D目标检测技术因其硬件成本优势和部署便捷性&#xff0c;正成为工业界关注的焦点。这项技术仅需单个摄像头即可实现对三维空间中物体的定位和…...