Java并发-06-AQS(AbstractQueuedSynchronizer)相关
1-概述
AQS全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者重写的方法。
1.1-主要特点
(1)用 state 属性来表示资源的状态(分独占模式和共享模式),子类需要定义如何维护这个状态,控制如何获取锁和释放锁。其中state的具体定义由子类(开发者)去设计。
getState - 获取 state 状态;
setState - 设置 state 状态;
compareAndSetState - cas 机制设置 state 状态;
独占模式是只有一个线程能够访问资源,而共享模式可以允许多个线程访问资源。
(2)提供了基于 FIFO 的等待队列,类似于 Monitor 的 EntryList
(3)条件变量来实现等待、唤醒机制,支持多个条件变量,类似于 Monitor 的 WaitSet。
1.2-AQS中重要的方法描述
| 方法名称 | 描述 |
| protected boolean tryAcquire(int arg) | 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后再进行 CAS 设置同步状态 |
| protected boolean tryRelease(int arg) | 独占式释放同步状态,等待获取同步状态的线程将有机会获取同步状态 |
| protected int tryAcquireShared(int arg) | 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败 |
| protected boolean tryReleaseShared(int arg) | 共享式释放同步状态 |
| protected boolean isHeldExclusively() | 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占 |
实现自定义同步组件时,将会调用同步器提供的模板方法,这些(部分)模板方法与描述如下:
| 方法名称 | 描述 |
| void acquire(int arg) | 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用重写的 tryAcquire(intarg)法 |
| void acquireInterruptibly(int arg) | 与acquire(int arg)相同,但是该方法响应中断,当前线程未获取到同步状态而进入同步队列中,如果当前线程被中断,则该方法会抛出InterruptedException 并返回 |
| boolean tryAcquireNanos(int arglong nanos) | 在acquireInterruptibly(int arg)基础上增加了超时限制,如果当前线程在超时时间内没有获取到同步状态,那么将会返回 false,如果获取到了返回 true |
| void acquireShared(int arg) | 共享式的获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态 |
| void acquireSharedInterruptibly(int arg) | 与acquireShared(int arg)相同,该方法响应中断 |
| boolean tryAcquireSharedNanos(intarg, long nanos) | 在acquireSharedInterruptibly(intarg)基础上增加了超时限制 |
| boolean release(int arg) | 独占式的释放同步状态该方法会在释放同步状态之后,将同步队列中第个节点包含的线程唤醒 |
| boolean releaseShared(int arg) | 共享式的释放同步状态 |
| Collection<Thread> getQueuedThreads) | 获取等待在同步队列上的线程集合 |
同步器提供的模板方法基本上分为3 类:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。自定义同步组件将使用同步器提供的模板方法来实现自己的同步语义。
1.3-设计思想
state设计:state 使用 volatile 配合 cas 保证其修改时的原子性;
阻塞恢复设计:park & unpark 来实现线程的暂停和恢复;
队列设计:使用了 FIFO 先入先出队列,并不支持优先级队列;借鉴了 CLH 队列,它是一种单向无锁队列。
2-基于AQS实现自定义锁
2.1-自定义实现不可重入排他锁
基于AQS可以快速实现自定义锁,下面就来实现一个 排他锁,不可重入。
public class MyLock1 implements Lock {static MySync1 mySync1=new MySync1();//尝试 加锁,不成功就进入等待队列@Overridepublic void lock() {mySync1.acquire(1);}//尝试,不成功,进入等待队列,可打断@Overridepublic void lockInterruptibly() throws InterruptedException {mySync1.acquireInterruptibly(1);}//尝试一次,不成功返回,不进入队列@Overridepublic boolean tryLock() {return mySync1.tryAcquire(1);}//尝试,不成功,进入等待队列,有时限@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return mySync1.tryAcquireNanos(1,unit.toNanos(time));}@Overridepublic void unlock() {mySync1.release(1);}@Overridepublic Condition newCondition() {return mySync1.newCondition();}//独占锁实现tryAcquire,tryRelease,isHeldExclusivelystatic class MySync1 extends AbstractQueuedSynchronizer{@Overrideprotected boolean tryAcquire(int acquire) {//我们可以设计state 为0 时表示 没有线程占用锁if(acquire==1){if(compareAndSetState(0,1)){setExclusiveOwnerThread(Thread.currentThread());//state字段是volatile 可以防止指令重排序 所以将线程设置 代码放置 在setState之前setState(1);return true;}}return false;}@Overrideprotected boolean tryRelease(int acquire) {//能够进入这个方法表示当前线程肯定已经获取到锁了if(acquire==1){if(getState()==0){throw new IllegalMonitorStateException();}setExclusiveOwnerThread(null);setState(0);return true;}return false;}protected Condition newCondition() {return new ConditionObject();}@Overrideprotected boolean isHeldExclusively() {return getState()==1;}}
}
测试排他性:
MyLock1 myLock1=new MyLock1();new Thread(()->{myLock1.lock();try {log.info("t1-lock");TimeUnit.SECONDS.sleep(1);}catch (Exception e){log.error("t1-error");}finally {myLock1.unlock();log.info("t1-unlock");}},"t1").start();new Thread(()->{myLock1.lock();try {log.info("t2-lock");}catch (Exception e){log.error("t2-error");}finally {myLock1.unlock();log.info("t2-unlock");}},"t2").start();
控制台输出:
15:36:17.590 [t1] INFO com.ycmy2023.aqs.demo01.TestDemo - t1-lock
15:36:18.595 [t2] INFO com.ycmy2023.aqs.demo01.TestDemo - t2-lock
15:36:18.595 [t2] INFO com.ycmy2023.aqs.demo01.TestDemo - t2-unlock
15:36:18.595 [t1] INFO com.ycmy2023.aqs.demo01.TestDemo - t1-unlock
由此可见:必须当线程1持有锁释放的时候,线程2才能获取到锁。
测试不可重入:
MyLock1 myLock1=new MyLock1();new Thread(()->{myLock1.lock();log.info("t1-获取锁1");myLock1.lock();log.info("t1-获取锁2");try {log.info("t1-lock");TimeUnit.SECONDS.sleep(1);}catch (Exception e){log.error("t1-error");}finally {myLock1.unlock();log.info("t1-unlock");}},"t1").start();
控制台输出:
15:38:42.028 [t1] INFO com.ycmy2023.aqs.demo01.TestDemo - t1-获取锁1
一直阻塞在第一个日志输出的地方,不会输出第二个日志,说明同一个线程获取到锁,不能再次加锁。
2.2-自定义实现共享锁
设计一个同步工具:该工具在同一时刻,只允许至多两个线程同时访问,超过两个线程的访问将被阻塞。
分析需求:
(1)确定访问模式。能够在同一时刻支持多个线程的访问,这显然是共享式访问,因此,需要使用同步器提供的acquireShared(int args)方法等和Shared 相关的方法这就要求必须重写 tryAcquireShared(int args)方法和 tryReleaseShared(int args)方法,这样才能保证同步器的共享式同步状态的获取与释放方法得以执行。
(2)定义资源数。在同一时刻允许至多两个线程的同时访问,表明同步资源数为2,这样可以设置初始状态 status 为2,当一个线进行获取,state 减1,该线程释放,则 state加1,状态的合法范围为 0、1和2。其中0表示当前已经有两个线获取了同步资源,此时再有其他线程对同步状态进行获取,该线程只能被阻塞。在同步状态变更时需要使用compareAndSet(int expect,int update)方法做原子性保障。
public class MySharedLock implements Lock {private static MySync2 sync=new MySync2(2);@Overridepublic void lock() {sync.acquireShared(1);}@Overridepublic void lockInterruptibly() throws InterruptedException {}@Overridepublic boolean tryLock() {return false;}@Overridepublic boolean tryLock(long time, TimeUnit unit) throws InterruptedException {return false;}@Overridepublic void unlock() {sync.releaseShared(1);}@Overridepublic Condition newCondition() {return null;}private static class MySync2 extends AbstractQueuedSynchronizer{MySync2(int count){setState(count);}@Overrideprotected int tryAcquireShared(int reduceCount) {for(;;){int current=getState();int newCount=current-reduceCount;if(newCount <0 || compareAndSetState(current,newCount)){return newCount;}}}@Overrideprotected boolean tryReleaseShared(int returnCount) {for(;;){int current=getState();int newCount=current+returnCount;if(compareAndSetState(current,newCount)){return true;}}}}
}
测试开发写的锁
MySharedLock lock=new MySharedLock();new Thread(()->{lock.lock();log.info("t1-获取锁1");try {log.info("t1-lock");TimeUnit.SECONDS.sleep(5);}catch (Exception e){log.error("t1-error");}finally {lock.unlock();log.info("t1-unlock");}},"t1").start();new Thread(()->{lock.lock();log.info("t2-获取锁1");try {log.info("t2-lock");TimeUnit.SECONDS.sleep(8);}catch (Exception e){log.error("t2-error");}finally {lock.unlock();log.info("t2-unlock");}},"t2").start();new Thread(()->{lock.lock();log.info("t3-获取锁1");try {log.info("t3-lock");TimeUnit.SECONDS.sleep(4);}catch (Exception e){log.error("t3-error");}finally {lock.unlock();log.info("t3-unlock");}},"t3").start();
控制台输出:
16:02:50.705 [t2] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t2-获取锁1
16:02:50.705 [t1] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t1-获取锁1
16:02:50.708 [t2] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t2-lock
16:02:50.708 [t1] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t1-lock
16:02:55.709 [t1] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t1-unlock
16:02:55.709 [t3] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t3-获取锁1
16:02:55.709 [t3] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t3-lock
16:02:58.717 [t2] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t2-unlock
16:02:59.716 [t3] INFO com.ycmy2023.aqs.demo02.TestDemo02 - t3-unlock
相关文章:
Java并发-06-AQS(AbstractQueuedSynchronizer)相关
1-概述 AQS全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并…...
【Python接口自动化】--深入了解HTTP接口基本组成和网页构建原理
引言 Python接口自动化有着广泛的应用场景,但是在实际使用过程中,可能会出现一些问题。比如,你不知道HTTP接口的基本构成,也不清楚网页是如何构建的。 这时,你就需要深入了解HTTP接口的基本组成和网页构建原理。通过本…...
window mysql5.7.27 启用SSL openssl mysql_ssl_rsa_setup
应客户监管部门要求 mysql必须要启用SSL。由于mysql安装在window上,启用过程中遇到了不少的坑,在此记录一下。 安装openssl 如果已经安装过可跳过此步 https://slproweb.com/download/Win64OpenSSL-1_1_1w.msi复制到浏览器下载后安装即可。如果需要其他…...
性能测试-JMeter分布式测试及其详细步骤
性能测试概要 性能测试是软件测试中的一种,它可以衡量系统的稳定性、扩展性、可靠性、速度和资源使用。它可以发现性能瓶颈,确保能满足业务需求。很多系统都需要做性能测试,如Web应用、数据库和操作系统等。 性能测试种类非常多,…...
学习gin-vue-admin之创建api和swagger
文章目录 go:generateViper 读写配置文件ZAP 保存日志定时任务创建apimodel步骤 1. 创建service步骤 2. 创建api步骤 3. 创建router 初始化总路由启动go-swagger路由配置swag init test将嵌套结构定义为指针或对象利弊结构体嵌套学习资源 go:generate //go:generate go env -w …...
2023-10-17 mysql-innodb-解析write_row的record的一行数据-分析
摘要: 2023-10-17 mysql-innodb-解析write_row的record的一行数据-分析. record是一行数据的序列化后的一整个字节流, 在innodb中需要解读出字段. 本文分析如何解析record, 以便学习这种技巧. row_mysql_store_col_in_innobase_format 调用堆栈: #0 row_mysql_store_col_in…...
认识web自动化测试!
1.什么是自动化测试? 自动化测试的概念: 软件自动化测试就是通过测试工具或者其他手段,按照测试人员的预定计划对软件产品进行自动化测试,他是软件测试的一个重要组成部分,能够完成许多手工测试无法完成或者难以实现的测试工作&a…...
多商户进驻小程序商城的作用是什么
多商户进驻商城简单来说就是在一个商城里,由经营者邀请同行、异业商家进驻到商城里(子商户),可丰富商城经营业态,满足客户多方购物需求,打造购物商圈及经营者获得更多收益等。 通过【雨科】平台的多商户进驻…...
接口响应慢该如何排查
不知道大家有没有遇到这种情况,接口业务逻辑写完后,用 postman 一调,发现接口响应时间好长,不得不对接口进行优化。但是此时接口的代码往往逻辑比较复杂,调用层次也比较多,很难定位到耗时较长的代码块。 遇…...
spring boot MongoDB实战
文章目录 项目搭建文章评论实体类的编写文章评论的基本增删改查根据上级ID查询文章评论的分页列表MongoTemplate实现评论点赞 GITHUB 项目搭建 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0&q…...
企业数字化转型时,会遇到的5大挑战
企业数字化转型时,会遇到的5大挑战添加链接描述 数字化转型已然是当今商业战略的一大基石,根据Gartner的《2023年度董事会调查》显示,有89%的企业将数字业务视为其增长的核心。但该研究的另一项统计数据也显示:在这些企业中&…...
动态语句 sqlserver
EXEC sp_executesql DynamicSQL, NFirstName NVARCHAR(50), LastName NVARCHAR(50), FirstName, LastName在EXEC sp_executesql语句中,后面的参数需要按特定顺序传递。这些参数的顺序如下: 1.第一个参数是动态SQL语句本身,通常是一个NVARCHA…...
【一文清晰】单元测试到底是什么?应该怎么做?
我是java程序员出身,后来因为工作原因转到到了测试开发岗位。测试开发工作很多年后,现在是一名自由职业者 1、什么是单元测试 2、该怎么做单元测试 一、什么是单元测试? 单元测试(unit testing),是指对软件…...
二、基于PCL的RANSAC拟合点云中所有直线或平面——3D点云处理系列
RANSAC原理:略。 其他博客大多都是介绍拟合单条直线或平面的代码案例,本文介绍如何拟合多条直线或平面,其实是在单个拟合的基础上接着拟合,以此类推。 注意:步骤中的直线模型是每次随机在点云中取点计算的。 步骤&…...
Linux实用指令-指定运行级别、帮助指令
一、 指定运行级别 1.运行级别说明: 0:关机 1:单用户[找回丢失密码] 2:多用户状态没有网络服务 3:多用户状态有网络服务 4:系统未使用保留给用户 5:图形界面 6:系统重启 常用运行级别是3和5,要修改默认的运行级别。可改文件/etc/inittab 的id:5:initd…...
【LeetCode】2562. 找出数组的串联值
难度:简单 题目 给你一个下标从 0 开始的整数数组 nums 。 现定义两个数字的 串联 是由这两个数值串联起来形成的新数字。 例如,15 和 49 的串联是 1549 。 nums 的 串联值 最初等于 0 。执行下述操作直到 nums 变为空: 如果 nums 中存…...
Hive知识梳理(好文)
Hive是建立在 Hadoop 上的数据仓库基础构架。可以将SQL查询转换为MapReduce的job在Hadoop集群上执行。 元数据 Hive元数据信息存储在Hive MetaStore中,或者mysql中。 分隔符 Hive默认的分格符有三种,分别是(Ctrl/A)、࿰…...
GitHub仓库的README文件无法显示图片问题-非域名污染原因
之前上自己仓库就偶然发现图片不显示现象,当时以为是网络问题就没有留意这事。但是一直不显示就有问题了!于是网上搜了一遭,看见大家遇到此现象的原因普遍归于DNS污染1而我的问题原来是MarkDown格式! 在图片语法前不要加分区语法…...
opencv入门到精通——图片,视频,摄像头的读取与保存
简介 OpenCV是一个流行的开源计算机视觉库,由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包,可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和计算机视觉应用。OpenCV主要使用C语言编写,同时也支持Python、Java、C等…...
Android 13.0 开机动画支持mp4格式视频作为开机动画播放
1.概述 在13.0的系统产品开发中,在系统开机动画这块一般情况下都是播放开机图片,然后绘制多张开机图片形成开机动画模式,而产品需求要求支持开机mp4格式的短视频来作为开机动画播放视频来介绍产品情况,就需要用开机视频来替代开机动画来实现功能 2.开机动画支持mp4格式视频…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
网络编程(Modbus进阶)
思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...
Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
6个月Python学习计划 Day 16 - 面向对象编程(OOP)基础
第三周 Day 3 🎯 今日目标 理解类(class)和对象(object)的关系学会定义类的属性、方法和构造函数(init)掌握对象的创建与使用初识封装、继承和多态的基本概念(预告) &a…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
