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.过…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...

《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...

内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
window 显示驱动开发-如何查询视频处理功能(三)
D3DDDICAPS_GETPROCAMPRANGE请求类型 UMD 返回指向 DXVADDI_VALUERANGE 结构的指针,该结构包含特定视频流上特定 ProcAmp 控件属性允许的值范围。 Direct3D 运行时在D3DDDIARG_GETCAPS的 pInfo 成员指向的变量中为特定视频流的 ProcAmp 控件属性指定DXVADDI_QUER…...
[特殊字符] Spring Boot底层原理深度解析与高级面试题精析
一、Spring Boot底层原理详解 Spring Boot的核心设计哲学是约定优于配置和自动装配,通过简化传统Spring应用的初始化和配置流程,显著提升开发效率。其底层原理可拆解为以下核心机制: 自动装配(Auto-Configuration) 核…...