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

Java 并发(二)—— AQS原理

AQS,全名AbstractQueuedSynchronizer。

  • 抽象队列同步器
  • 定义多线程访问共享资源的同步模板,解决了实现自定义同步器时涉及的大量细节问题,简化开发
  • 两种同步状态:独占、共享
  • 核心组件:State变量、CLH变体队列、获取 / 释放资源 方法重写

一、State变量

    private volatile int state;//返回同步状态protected final int getState() {return state;}//设置同步状态protected final void setState(int newState) {state = newState;}//使用CAS设置同步状态protected final boolean compareAndSetState(int expect, int update) {return STATE.compareAndSet(this, expect, update);}

用关键字volatile修饰state表示该共享资源的状态一更改就能被所有线程可见。

state为0时代表线程可以竞争锁,不为0时代表当前对象锁已经被占有。

  • ReentrantLockstate用来表示是否有锁资源
  • ReentrantReadWriteLockstate16位代表读锁状态,低16位代表写锁状态
  • Semaphorestate用来表示可用信号的个数
  • CountDownLatchstate用来表示计数器的值

二、CLH变体队列

AQS 维护一个等待的线程队列: 

  • FIFO先进先出)队列,保证公平性
  • 双向链表形式,方便尾部节点插入

当一个线程竞争资源失败,就会将等待资源的线程封装成一个Node节点,通过CAS原子操作插入队列尾部,最终不同的Node节点连接组成了一个CLH队列,这些线程会被UNSAFE.park()操作挂起,等待其他获取锁的线程释放锁才能够被唤醒。

waitStatus

三、获取 / 释放资源 方法重写

AQS在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。

不同的自定义同步器争用共享资源的方式也不同。自定义同步器在实现时只需要实现共享资源state的获取与释放方式即可,至于具体线程等待队列的维护(如获取资源失败入队/唤醒出队等),AQS已经在顶层实现好了。自定义同步器实现时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

1.独占资源(不响应线程中断)

  • acquire(int arg):独占式获取资源模板

此方法是独占模式下线程获取共享资源的顶层入口。如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。

函数流程如下:

  1. tryAcquire()尝试直接去获取资源,如果成功则直接返回(这里体现了非公平锁,每个线程获取锁时会尝试直接抢占加塞一次,而CLH队列中可能还有别的线程在等待);
  2. addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
  3. acquireQueued()使线程阻塞在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
  4. 如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。

  • release(int arg):独占式释放资源模板

函数流程如下:

  1. tryRelease()尝试直接释放资源,如果成功(state=0),要返回true,否则返回false。
  2. unparkSuccessor(Node) 唤醒等待队列中下一个线程。

2.共享资源(不响应线程中断)

  • acquireShared(int arg):共享式获取资源模板

函数流程如下:

  1. tryAcquireShared()尝试获取资源,成功则直接返回;
  2. 失败则通过doAcquireShared()进入等待队列park(),直到被unpark()/interrupt()并成功获取到资源才返回。整个等待过程也是忽略中断的。

跟acquire()的流程大同小异,只不过多了个自己拿到资源后,还会去唤醒后继队友的操作(这才是共享嘛)

按照正常的思维,共享模式是可以多个线程同时执行的才对,所以,多个线程的情况下,如果老大释放完资源,但这部分资源满足不了老二,但能满足老三,那么老三就可以拿到资源。可事实是,从源码设计中可以看出,如果真的发生了这种情况,老三是拿不到资源的,因为等待队列是按顺序排列的,老二的资源需求量大,会把后面量小的老三以及老四、老五等都给卡住。从这一个角度来看,虽然AQS严格保证了顺序,但也降低了并发能力


  • releaseShared(int arg):共享式释放资源模板

函数流程如下:

  1. tryReleaseShared:释放资源。
  2. doAcquireShared:唤醒后继结点。

四、应用实例:Mutex(互斥锁)

class Mutex implements Lock, java.io.Serializable {// 自定义同步器private static class Sync extends AbstractQueuedSynchronizer {// 判断是否锁定状态protected boolean isHeldExclusively() {return getState() == 1;}// 尝试获取资源,立即返回。成功则返回true,否则false。public boolean tryAcquire(int acquires) {assert acquires == 1; // 这里限定只能为1个量if (compareAndSetState(0, 1)) {//state为0才设置为1,不可重入!setExclusiveOwnerThread(Thread.currentThread());//设置为当前线程独占资源return true;}return false;}// 尝试释放资源,立即返回。成功则为true,否则false。protected boolean tryRelease(int releases) {assert releases == 1; // 限定为1个量if (getState() == 0)//既然来释放,那肯定就是已占有状态了。只是为了保险,多层判断!throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);//释放资源,放弃占有状态return true;}}// 真正同步类的实现都依赖继承于AQS的自定义同步器!private final Sync sync = new Sync();//lock<-->acquire。两者语义一样:获取资源,即便等待,直到成功才返回。public void lock() {sync.acquire(1);}//tryLock<-->tryAcquire。两者语义一样:尝试获取资源,要求立即返回。成功则为true,失败则为false。public boolean tryLock() {return sync.tryAcquire(1);}//unlock<-->release。两者语文一样:释放资源。public void unlock() {sync.release(1);}//锁是否占有状态public boolean isLocked() {return sync.isHeldExclusively();}
}

同步类在实现时一般都将自定义同步器(sync)定义为内部类,供自己使用;而同步类自己(Mutex)则实现某个接口,对外服务。

下面这些类中都包含Sync内部类。

五、参考

Java并发之AQS详解 - waterystone - 博客园 (cnblogs.com)

谈谈Java多线程离不开的AQS_java aqs-CSDN博客

Java并发编程面试题 | 小林coding (xiaolincoding.com)

相关文章:

Java 并发(二)—— AQS原理

AQS&#xff0c;全名AbstractQueuedSynchronizer。 抽象队列同步器定义多线程访问共享资源的同步模板&#xff0c;解决了实现自定义同步器时涉及的大量细节问题&#xff0c;简化开发两种同步状态&#xff1a;独占、共享核心组件&#xff1a;State变量、CLH变体队列、获取 / 释…...

Maven插件:exec-maven-plugin-代码执行或者直接输出内置变量信息

文章目录 概述使用应用自行实现记录项目打包插件 概述 官网&#xff1a; https://www.mojohaus.org/exec-maven-plugin/usage.html   依赖&#xff1a; https://mvnrepository.com/artifact/org.codehaus.mojo/exec-maven-plugin 使用 <plugin><groupId>org.codeh…...

https://ffmpeg.org/

https://ffmpeg.org/ https://www.gyan.dev/ffmpeg/builds/ https://github.com/BtbN/FFmpeg-Builds/releases F:\Document_ffmpeg F:\Document_ffmpeg\ffmpeg-master-latest-win64-gpl-shared\bin...

linux 源码部署polardb-x 错误汇总

前言 在linux 源码部署polardb-x 遇到不少错误&#xff0c;特在此做个汇总。 问题列表 CN 启动报错 Failed to init new TCP 详细错误如下 Caused by: Failed to init new TCP. XClientPool to my_polarx#267b21d8127.0.0.1:33660 now 0 TCP(0 aging), 0 sessions(0 runni…...

vscode用快捷键一键生成vue模板

项目中有些代码模块是固定的&#xff0c;如下面的代码所示&#xff0c;为了不重复写这些相同的代码&#xff0c;我们可以使用快键键一键生成模板。 流程&#xff1a; 中文&#xff1a;首选项-> 用户代码片段 -> 输入框中输入vue,找到vue.json文件&#xff08;没有vue.j…...

ARM 架构硬件新趋势:嵌入式领域的未来

目录 目录 一、ARM 架构概述 二、新趋势一&#xff1a;AI 加速器集成 三、新趋势二&#xff1a;更高效的电源管理 四、新趋势三&#xff1a;安全性增强 五、结语 随着物联网 (IoT) 和边缘计算的发展&#xff0c;ARM 架构在嵌入式系统中的应用越来越广泛。从智能手机到智能…...

星戈瑞-二油酰磷脂酰乙醇胺标记荧光素 DOPE-FITC

DOPE-FITC&#xff0c;全称为1,2-dioleoyl-sn-glycero-3-phosphoethanolamine-N-FITC&#xff0c;是一种结合了二油酰磷脂酰乙醇胺&#xff08;DOPE&#xff09;与荧光素异硫氰酸酯&#xff08;FITC&#xff09;的复合标记物。以其独特的磷脂结构和强烈的绿色荧光特性&#xff…...

堆的实现(偷懒版)

&#x1f339;个人主页&#x1f339;&#xff1a;喜欢草莓熊的bear &#x1f339;专栏&#x1f339;&#xff1a;数据结构 目录 前言 一、堆的实现 1.1 堆的向下调整算法 思路&#xff1a; 1.2 堆的向上调整算法 1.3 堆的创建 1.4 堆的复杂度计算 向下调整建堆的复杂度…...

一键启动,智能分拣:3D视觉系统赋能多SKU纸箱高效混拆作业

在快速发展的电商时代&#xff0c;仓储物流面临着前所未有的挑战。尤其是面对成千上万种不同的纸箱&#xff0c;如何实现快速、准确、高效的混拆作业&#xff0c;成为了众多企业亟待解决的问题。幸运的是&#xff0c;随着科技的进步&#xff0c;3D视觉系统正逐步成为这一领域的…...

unity草体渲染方案 GPU Instaning

有一天看项目里的FrameDebug发现在森林系的场景里草体的drawcall差不多有100多 主要是因为灯光贴图&#xff0c;位置等不一样导致的打断合批&#xff0c;导致一个批次只能渲染10个左右的草体 之前有了解过unity有接口&#xff08;Graphics.DrawMeshInstanced&#xff09;可以把…...

最近在西安召开的学术会议:EI检索超快,信息系统与计算技术领域!

第十二届信息系统与计算技术国际会议&#xff08;ISCTech 2024&#xff09;将于2024年11月8日-11月11日在中国西安盛大举行&#xff0c;由长沙理工大学主办&#xff0c;同济大学、西北工业大学联合协办。会议聚焦信息系统与计算技术等相关研究领域&#xff0c;广泛邀请国内外知…...

sRGB和伽马矫正

sRGB和伽马矫正 1. sRGB的含义&#xff1a; sRGB是一种色彩空间&#xff0c;全称为“标准红色-绿色-蓝色”&#xff08;standard Red Green Blue&#xff09;。它由惠普和微软在1996年共同开发&#xff0c;用于确保不同设备上色彩的一致性。 在sRGB中&#xff0c;“s”代表“…...

Summer School science communication project--Laptop Selection Suggestion

目录 Introduction Audiance Usage CPU What is a central processing unit (CPU) Notable makers of CPUs GPU Graphics Card: GPU The classifications of graphics cards The brands of graphics cards Dedicated Graphics Cards GeForce MX Series: GeForc…...

网络编程概念详解模拟回显客户端服务器

目录 1.网络中重要的概念 1&#xff09;IP地址&#xff1a; 2&#xff09;端口号&#xff1a; 3&#xff09;协议 协议分层 OSI七层模型(教科书) TCP/IP五层模型 封装和分用 网络套接字 面试题&#xff1a;TCP/UDP的区别&#xff1f; UDP数据报套接字编程 模拟一个回…...

代码随想录第二十四天|动态规划(8)

目录 LeetCode 300. 最长递增子序列 LeetCode 674. 最长连续递增序列 LeetCode 718. 最长重复子数组 LeetCode 1143. 最长公共子序列 LeetCode 1035. 不相交的钱 LeetCode 53. 最大子序和 LeetCode 392. 判断子序列 总结 LeetCode 300. 最长递增子序列 题目链接&#…...

编程-设计模式 3:单例模式

设计模式 3&#xff1a;单例模式 定义与目的 定义&#xff1a;单例模式确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问该实例。目的&#xff1a;这种模式通常用于那些需要频繁访问且只需一个实例的对象&#xff0c;例如配置管理器、日志记录器等。 实现示例…...

Kaniko 构建 Docker 镜像

Kaniko 主要用于构建 Docker 镜像&#xff0c;而不是运行程序。它的主要用途是从 Dockerfile 构建容器镜像&#xff0c;但它并不负责运行容器或程序。以下是 Kaniko 的主要功能和局限性&#xff1a; 主要功能 构建镜像&#xff1a;Kaniko 从 Dockerfile 构建容器镜像。它通过…...

Javascript常见算法(每日两个)

合并两个有序链表 在JavaScript中&#xff0c;合并两个有序链表通常指的是将两个已经按照某种顺序&#xff08;如升序或降序&#xff09;排列的链表合并成一个新的有序链表。由于JavaScript本身不直接支持链表数据结构&#xff0c;我们通常会用对象或数组来模拟链表的行为。但…...

Spring -- 事务

Spring中事务的操作分为两类:(1)编程式事务 – 手动写代码操作事务(2)声明式事务 – 利用注解开启事务和提交事务 1. 编程式事务 准备Controller RestController RequestMapping("/user") public class UserInfoController {Autowiredprivate UserInfoService use…...

生命密码的破译者:AI如何学会读懂DNA语言?

引言 如果能像解读一本神秘的书籍那样&#xff0c;理解DNA的“语言”&#xff0c;将是多么令人兴奋的科学突破&#xff01;如今&#xff0c;这正在逐步变为现实。科学家们训练出的AI模型GROVER正如一个勤奋的学生&#xff0c;学习着DNA的每一个“单词”和“语法”&#xff0c;…...

挑战杯推荐项目

“人工智能”创意赛 - 智能艺术创作助手&#xff1a;借助大模型技术&#xff0c;开发能根据用户输入的主题、风格等要求&#xff0c;生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用&#xff0c;帮助艺术家和创意爱好者激发创意、提高创作效率。 ​ - 个性化梦境…...

【网络】每天掌握一个Linux命令 - iftop

在Linux系统中&#xff0c;iftop是网络管理的得力助手&#xff0c;能实时监控网络流量、连接情况等&#xff0c;帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

(转)什么是DockerCompose?它有什么作用?

一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...

自然语言处理——文本分类

文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益&#xff08;IG&#xff09; 分类器设计贝叶斯理论&#xff1a;线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别&#xff0c; 有单标签多类别文本分类和多…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...