Java中的AQS
文章目录
- 什么是AQS
- AbstractQueuedSynchronizer方法解析
- 自旋与阻塞
- ReentrantLock,Semaphore以及CountDownLatch对比
- ReentrantLock实现原理
- 原理
- ReentrantLock源码中compareAndSetState的方法
- Semaphore实现原理
- CountDownLatch实现原理
什么是AQS
AQS是Java中的一个抽象队列同步器(AbstractQueuedSynchronizer)类,它提供了一种实现同步器的框架和实现方式。它是Java并发编程中的一个重要组成部分,广泛用于实现ReentrantLock、Semaphore、CountDownLatch等同步工具类。
AQS的核心思想是利用一个先进先出(FIFO)的双向队列来管理线程的竞争和等待。AQS提供了两种模式:独占模式和共享模式。独占模式是指只有一个线程可以持有同步状态,如ReentrantLock;共享模式是指多个线程可以同时持有同步状态,如Semaphore。
AQS的具体实现方式是通过维护一个volatile变量state表示同步状态,当state为0时表示没有线程占用同步状态,当state为1时表示有一个线程占用同步状态。当多个线程竞争同步状态时,只有一个线程可以成功占用同步状态,其余线程将加入到AQS的同步队列中等待。当占用同步状态的线程释放同步状态时,AQS会从同步队列中选择一个线程唤醒,使其重新尝试获取同步状态。
总之,AQS提供了一种高效且灵活的实现同步器的方式,可以满足不同的并发编程需求。
AbstractQueuedSynchronizer方法解析
AbstractQueuedSynchronizer(AQS)是Java并发编程中的一个基础框架,它提供了一种实现同步器的通用方法。AQS内部维护了一个同步队列,通过“自旋”和“阻塞”两种方式来实现同步操作。
AQS提供了一些核心方法,其含义和作用如下:
-
acquire(int arg): 尝试获取同步状态,如果获取失败则加入同步队列并阻塞等待唤醒,直到获取同步状态成功。参数arg表示获取同步状态所需的资源数量。
-
tryAcquire(int arg): 尝试获取同步状态,如果获取成功则返回true,否则返回false。参数arg表示获取同步状态所需的资源数量。
-
release(int arg): 释放同步状态,通知其他线程可以尝试获取同步状态。参数arg表示释放的资源数量。
-
tryRelease(int arg): 尝试释放同步状态,如果释放成功则返回true,否则返回false。参数arg表示释放的资源数量。
-
acquireInterruptibly(int arg): 尝试获取同步状态,如果获取失败则加入同步队列并阻塞等待唤醒,直到获取同步状态成功或者被中断。参数arg表示获取同步状态所需的资源数量。
-
acquireShared(int arg): 尝试获取共享同步状态,如果获取失败则加入同步队列并阻塞等待唤醒,直到获取共享同步状态成功。参数arg表示获取共享同步状态所需的资源数量。
-
tryAcquireShared(int arg): 尝试获取共享同步状态,如果获取成功则返回非负数,否则返回负数。返回值表示当前线程获取到的共享资源数量。参数arg表示获取共享同步状态所需的资源数量。
-
releaseShared(int arg): 释放共享同步状态,通知其他线程可以尝试获取共享同步状态。参数arg表示释放的资源数量。
-
tryAcquireNanos(int arg, long nanosTimeout): 在规定时间内尝试获取同步状态,如果获取失败则加入同步队列并阻塞等待唤醒,直到获取同步状态成功或者超时。参数arg表示获取同步状态所需的资源数量,参数nanosTimeout表示等待超时时间。
AQS提供了一些核心方法来实现同步操作,可以用于实现不同类型的同步器,如ReentrantLock、Semaphore、CountDownLatch等。这些方法可以满足不同的并发编程需求,需要根据具体的场景选择合适的同步方式和同步策略。
自旋与阻塞
自旋和阻塞是Java并发编程中两种不同的线程等待方式。
自旋是指线程在等待某个条件满足时,不断地循环检查条件是否满足,如果不满足就一直循环等待,直到条件满足。自旋的好处是可以减少线程上下文切换的开销,因为线程一直处于执行状态,不需要进行线程状态的切换。但是自旋需要占用CPU资源,如果自旋时间过长会导致CPU资源浪费,降低系统性能。
阻塞是指线程在等待某个条件满足时,将自己挂起,不再占用CPU资源,直到条件满足时再被唤醒继续执行。阻塞的好处是可以释放CPU资源,避免浪费,但是阻塞需要进行线程状态的切换,如果线程频繁地阻塞和唤醒会增加系统开销。
在Java中,阻塞通常是通过调用wait()方法或者阻塞式IO来实现的,而自旋通常是在锁等待时实现的。例如,在ReentrantLock中,当线程尝试获取锁失败时,它会在同步队列中自旋等待锁的释放,直到锁被释放或者等待时间超过一定阈值才会阻塞等待。因此,在选择线程等待方式时需要根据具体的场景和需求进行权衡和选择
ReentrantLock,Semaphore以及CountDownLatch对比
ReentrantLock、Semaphore和CountDownLatch都是Java并发编程中常用的同步工具类,它们都使用了AQS的实现方式。
ReentrantLock(可重入锁):是一种独占锁,它允许一个线程多次获取锁,支持公平锁和非公平锁。与synchronized关键字相比,ReentrantLock提供了更多的灵活性和功能,如可中断锁、限时锁、公平锁等。
Semaphore(信号量):是一种共享锁,它用于控制对资源的访问数量。Semaphore维护了一个计数器,当有线程获取信号量时,计数器减1,当计数器为0时,其他线程需要等待。Semaphore可以用于实现限流、资源池等功能。
CountDownLatch(倒计时器):是一种同步工具类,它可以让一个或多个线程等待其他线程执行完毕后再继续执行。CountDownLatch维护了一个计数器,当计数器为0时,等待的线程可以继续执行。它可以用于协调多个线程的执行顺序。
总之,ReentrantLock、Semaphore和CountDownLatch都是Java并发编程中非常有用的同步工具类,可以帮助我们更好地控制多线程的并发访问和协调多线程的执行。在使用这些工具类时,需要注意线程安全和性能问题,以及选择合适的同步策略。
ReentrantLock实现原理
原理
获取锁:当一个线程请求获取锁时,ReentrantLock会首先尝试获取锁,如果锁未被占用,则该线程可以立即获取锁;否则,该线程将被加入到同步队列中等待获取锁。
可重入性:如果当前线程已经持有锁,那么它可以重复获取该锁,而不需要重新等待。为了实现可重入性,ReentrantLock需要维护一个记录锁持有者的ThreadLocal变量,以及一个记录锁持有次数的计数器。
公平性和非公平性:ReentrantLock支持公平锁和非公平锁。公平锁是指多个线程获取锁的顺序与它们加入同步队列的顺序相同;非公平锁则不保证获取锁的顺序与加入队列的顺序相同。在公平锁模式下,线程获取锁的顺序是有序的,但是会降低并发性能;在非公平锁模式下,线程获取锁的顺序是不确定的,但是可以提高并发性能。
释放锁:当一个线程释放锁时,ReentrantLock会将state变量置为0,以表示该锁已经被释放。同时,它会从同步队列中选择一个等待的线程唤醒,使其重新尝试获取锁。如果当前线程还持有该锁,那么需要将计数器减1,直到计数器为0才能完全释放锁。
ReentrantLock源码中compareAndSetState的方法
在ReentrantLock的源码中,比较并交换(CompareAndSet)状态值是实现锁的核心部分之一。在ReentrantLock中,状态值的改变可以表示锁的获取和释放,因此状态值的比较并交换是实现锁的关键。
在ReentrantLock中,状态值是由AQS(AbstractQueuedSynchronizer)的内部类Node中的state字段表示的。具体来说,当线程获取锁时,它会创建一个Node节点并尝试将状态值从0(表示锁未被占用)改变为1(表示锁已被占用)。当线程释放锁时,它会将状态值从1改变为0,表示锁已被释放。
在AQS中,compareAndSetState(int expect, int update)方法用于比较并交换状态值。具体来说,该方法会比较当前状态值是否等于expect,如果是则将状态值修改为update,否则不进行修改。这个方法是原子的,可以保证状态值的改变是线程安全的。在ReentrantLock中,使用compareAndSetState方法实现锁的获取和释放,比如在获取锁时将状态值从0修改为1,在释放锁时将状态值从1修改为0。
下面是ReentrantLock源码中compareAndSetState方法的具体实现:
protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
该方法调用了unsafe类的compareAndSwapInt方法,该方法是一个本地方法,用于实现原子的比较并交换操作。其中,this表示当前对象,stateOffset表示state字段在对象中的偏移量,expect表示期望的状态值,update表示要修改的状态值。如果当前状态值等于expect,则将状态值修改为update并返回true,否则返回false。
总之,ReentrantLock中的compareAndSetState方法实现了状态值的原子性修改,是实现锁的关键部分之一。
Semaphore实现原理
Semaphore是一个计数信号量,它可以用于控制同时访问某个资源的线程数量。Semaphore内部维护了一个计数器,表示可以访问资源的线程数量,线程在访问资源时需要先获取Semaphore的许可,当计数器的值大于0时,线程可以获取许可并访问资源,计数器的值减一;当计数器的值为0时,线程需要等待其他线程释放许可才能获取许可并访问资源。
Semaphore的实现原理主要是基于AQS(AbstractQueuedSynchronizer)类,Semaphore通过AQS实现了许可的获取和释放,并且保证了线程之间的互斥和同步。
Semaphore的实现可以分为两个部分:获取许可和释放许可。
- 获取许可
Semaphore的acquire方法用于获取许可,如果当前有可用的许可则获取成功,如果没有则线程会被阻塞等待。acquire方法的具体实现如下:
public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
acquire方法内部调用了AQS的acquireSharedInterruptibly方法,该方法实现了阻塞等待。如果当前计数器的值大于0,线程可以获取许可并将计数器的值减一,如果计数器的值为0,则线程会被阻塞等待其他线程释放许可。在AQS中,线程的阻塞等待是通过将线程添加到同步队列中实现的。
- 释放许可
Semaphore的release方法用于释放许可,如果当前没有线程等待许可则将许可的数量加一,如果有线程等待许可则唤醒一个等待线程并将许可的数量加一。release方法的具体实现如下:
public void release() {sync.releaseShared(1);
}
release方法内部调用了AQS的releaseShared方法,该方法实现了许可的释放和等待线程的唤醒。如果当前有等待线程,则会从同步队列中唤醒一个线程,让其获取许可并执行;如果当前没有等待线程,则将许可的数量加一。
Semaphore是基于AQS实现的一个计数信号量,通过计数器实现了对许可的控制,并通过同步队列实现了线程之间的同步和互斥。Semaphore的实现为线程的访问资源提供了一个简单而可靠的机制。
CountDownLatch实现原理
CountDownLatch是一种同步工具,它可以让某个线程一直等待直到其他所有线程都执行完毕。CountDownLatch内部维护了一个计数器,线程在执行完毕后调用CountDownLatch的countDown方法将计数器的值减一,等待线程调用await方法等待计数器的值变为0,当计数器的值变为0时,等待线程继续执行。
CountDownLatch的实现原理主要是基于AQS(AbstractQueuedSynchronizer)类,CountDownLatch通过AQS实现了计数器的减少和等待,并且保证了线程之间的互斥和同步。
CountDownLatch的实现可以分为两个部分:计数器的减少和等待计数器变为0。
- 计数器的减少
CountDownLatch的countDown方法用于减少计数器的值,该方法会将计数器的值减一,并唤醒正在等待的线程。countDown方法的具体实现如下:
public void countDown() {sync.releaseShared(1);
}
countDown方法内部调用了AQS的releaseShared方法,该方法实现了许可的释放和等待线程的唤醒。如果当前有等待线程,则会从同步队列中唤醒一个线程,让其执行;如果当前没有等待线程,则将许可的数量加一。
- 等待计数器变为0
CountDownLatch的await方法用于等待计数器的值变为0,如果当前计数器的值不为0,则等待线程会被阻塞等待。await方法的具体实现如下:
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
await方法内部调用了AQS的acquireSharedInterruptibly方法,该方法实现了阻塞等待。如果当前计数器的值为0,线程可以直接继续执行;如果计数器的值不为0,则线程会被阻塞等待其他线程调用countDown方法将计数器的值减少。
总之,CountDownLatch是基于AQS实现的一个同步工具,通过计数器实现了对线程的控制,并通过同步队列实现了线程之间的同步和互斥。CountDownLatch的实现为多个线程之间的同步提供了一个简单而可靠的机制。
相关文章:
Java中的AQS
文章目录什么是AQSAbstractQueuedSynchronizer方法解析自旋与阻塞ReentrantLock,Semaphore以及CountDownLatch对比ReentrantLock实现原理原理ReentrantLock源码中compareAndSetState的方法Semaphore实现原理CountDownLatch实现原理什么是AQS AQS是Java中的一个抽象…...

Spring——案例-业务层接口执行效率和AOP通知获取数据+AOP总结
执行时间获取:记录开始时间和结束时间,取差值。 这里使用环绕通知来实现。 环境准备: 项目文件结构: 业务层接口和实现类: 数据层: 采用mybatis注解开发,这里没有实现类,直接在接口方法里面实现映射。 domain层: 实现了数据库里面每一个…...

国外SEO舆情处理最佳黄金时间
在国外市场,SEO(搜索引擎优化)的舆情处理是非常重要的,因为它可以帮助提高网站的排名和流量,并且建立品牌的声誉和信誉。 然而,在什么时间进行舆情处理是一个值得探讨的问题。 在本文中,我们将…...

ROC和AUC
目录 ROC AUC ROC ROC曲线是Receiver Operating Characteristic Curve的简称,中文名为"受试者工作特征曲线"。ROC曲线的横坐标为假阳性率(False Postive Rate, FPR);纵坐标为真阳性率(True Positive Rate, TPR).FPR和TPR的计算方法分别为 F…...
Dopamine-PEG-cRGD,DOPA-PEG-cRGD,多巴胺-聚乙二醇-crgd细胞穿膜肽
名称:多巴胺-聚乙二醇-cRGD穿膜肽,多巴胺-聚乙二醇-crgd细胞穿膜肽英文名称:Dopamine-PEG-cRGD,DOPA-PEG-cRGD规格:50mg,100mg,150mg(根据要求可定制)描述:cRGD多肽序列: cyclo(RGDfK)外 观 : 半固体或固体,取决于分子量。溶解性:…...

动态规划回文子串
647. 回文子串方法:双指针回文子串有长度为奇数和偶数两种,extend(s, i, i, n); extend(s, i, i 1, n);就分别对应长度为奇数和偶数的情况class Solution { private:int extend(const string& s, int i, int j, int n) {int res 0;while (i > 0…...

windows 域控提权CVE-2014-6324CVE-2020-1472CVE-2021-42287CVE-2022-26923
一、CVE-2014-6324复现 环境:god.org域,两台主机,一台win2008域控,另一台web服务器win2008 工具:ms14-068.exe(漏洞exp) mimikatz psexec 利用条件: 1.域用户账号密码 2.获得一台主机权限(本地administ…...

1、JDK 安装 Java环境变量配置
jdk下载(Java8) (下载时间不同,小版本号会有变化,不影响后续安装) 官网下载地址:https://www.oracle.com/java/technologies/downloads/#java8-windows 下载完后安装 JDK 环境变量配置 Win…...

[c++]list模拟实现
目录 前言: 学习类的方式: 1 类成员变量 1.1 list成员变量 1.2 结点结构体变量 1.3 迭代器成员变量 2 默认函数——构造 2.1 结点结构体构造函数 2.2 list构造函数 2.3 迭代器构造函数 3 迭代器实现 3.1 list部分 3.2 迭代器结构体部分 3.2…...

实用的仓库管理软件有哪些,盘点2023年5大仓库管理软件!
对于做批发生意的老板或工厂老板来说,选择一款实用的仓库管理软件是至关重要的。仓库管理软件除了可以帮你降低仓库管理成本,提高经营管理的效率,还能够在手机上随时随地掌控仓库员工和商品的最新信息,与客户、供应商的订单情况能…...
(八十二)透彻研究通过explain命令得到的SQL执行计划(1)
今天我们正式进入研究explain命令得到的SQL执行计划的内容了,只要把explain分析得到的SQL执行计划都研究透彻,完全能看懂,知道每个执行计划在底层是怎么执行的,那么后面学习SQL语句的调优就非常容易了。 首先,我们现在…...

【Linux】旋转锁 | 读写锁
在之前的线程学习中,用到的锁都是挂起等待锁,如果申请不到锁,那就会在锁中等待; 自旋锁则不大相似 文章目录1.自旋锁1.1 概念1.2 接口1.2.1 pthread_spin_init/destroy1.2.2 pthread_spin_lock1.2.3 pthread_spin_unlock2.读写锁…...

EasyExcell导出excel添加水印
EasyExcell导出excel添加水印1、添加easyExcel相关依赖2、准备基础工具类3、创建水印handler类4、创建单元测试类WriteTest.class5、测试结果1、添加easyExcel相关依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId&…...

SpringCloud:Nacos配置管理
Nacos除了可以做注册中心,同样可以做配置管理来使用。 1.1.统一配置管理 当微服务部署的实例越来越多,达到数十、数百时,逐个修改微服务配置就会让人抓狂,而且很容易出错。我们需要一种统一配置管理方案,可以集中管理…...

正则表达式引擎NFA自动机的回溯解决方案总结
前几天线上一个项目监控信息突然报告异常,上到机器上后查看相关资源的使用情况,发现 CPU 利用率将近 100%。通过 Java 自带的线程 Dump 工具,我们导出了出问题的堆栈信息。 我们可以看到所有的堆栈都指向了一个名为 validateUrl 的方法&#…...

卷积神经网络之AlexNet
目录概述AlexNet特点激活函数sigmoid激活函数ReLu激活函数数据增强层叠池化局部相应归一化DropoutAlexnet网络结构网络结构分析AlexNet各层参数及其数量模型框架形状结构关于数据集训练学习keras代码示例概述 由于受到计算机性能的影响,虽然LeNet在图像分类中取得了…...
React中setState什么时候是同步的,什么时候是异步的?
本文内容均针对于18.x以下版本 setState 到底是同步还是异步?很多人可能都有这种经历,面试的时候面试官给了你一段代码,让你说出输出的内容,比如这样: constructor(props) {super(props);this.state {val: 0}}compo…...

优秀开源软件的类,都是怎么命名的?
日常编码中,代码的命名是个大的学问。能快速的看懂开源软件的代码结构和意图,也是一项必备的能力。 Java项目的代码结构,能够体现它的设计理念。Java采用长命名的方式来规范类的命名,能够自己表达它的主要意图。配合高级的 IDEA&…...

绘制CSP的patterns矩阵图
最近在使用FBCSP处理数据,然后就想着看看处理后的样子,用地形图的形式表现出来,但是没有符合自己需求的函数可以实现,就自己尝试的实现了一下,这里记录一下,方便以后查阅。 绘制CSP的patterns矩阵图 对数据做了FBCSP处理,但是想画一下CSP计算出来的patterns的地形图,并…...

Datatables展示数据(表格合并、日期计算、异步加载数据、分页显示、筛选过滤)
系列文章目录 datatable 自定义筛选按钮的解决方案Echarts实战案例代码(21):front-endPage的CJJTable前端分页插件ajax分页异步加载数据的解决方案 文章目录系列文章目录前言一、html容器构建1.操作按钮2.表格构建二、时间日期计算三、dataTables属性配置1.调用2.过…...

Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...

让回归模型不再被异常值“带跑偏“,MSE和Cauchy损失函数在噪声数据环境下的实战对比
在机器学习的回归分析中,损失函数的选择对模型性能具有决定性影响。均方误差(MSE)作为经典的损失函数,在处理干净数据时表现优异,但在面对包含异常值的噪声数据时,其对大误差的二次惩罚机制往往导致模型参数…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...

AD学习(3)
1 PCB封装元素组成及简单的PCB封装创建 封装的组成部分: (1)PCB焊盘:表层的铜 ,top层的铜 (2)管脚序号:用来关联原理图中的管脚的序号,原理图的序号需要和PCB封装一一…...

如何把工业通信协议转换成http websocket
1.现状 工业通信协议多数工作在边缘设备上,比如:PLC、IOT盒子等。上层业务系统需要根据不同的工业协议做对应开发,当设备上用的是modbus从站时,采集设备数据需要开发modbus主站;当设备上用的是西门子PN协议时…...

二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...

2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
1、获取景点详情的请求【my_api.js】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http(/login/getWXSessionKey, {code,avatar}); };//…...
Docker环境下安装 Elasticsearch + IK 分词器 + Pinyin插件 + Kibana(适配7.10.1)
做RAG自己打算使用esmilvus自己开发一个,安装时好像网上没有比较新的安装方法,然后找了个旧的方法对应试试: 🚀 本文将手把手教你在 Docker 环境中部署 Elasticsearch 7.10.1 IK分词器 拼音插件 Kibana,适配中文搜索…...

SQLSERVER-DB操作记录
在SQL Server中,将查询结果放入一张新表可以通过几种方法实现。 方法1:使用SELECT INTO语句 SELECT INTO 语句可以直接将查询结果作为一个新表创建出来。这个新表的结构(包括列名和数据类型)将与查询结果匹配。 SELECT * INTO 新…...