Java多线程篇(5)——cas和atomic原子类
文章目录
- CAS
- Atomic 原子类
- 一般原子类
- 针对aba问题 —— AtomicStampedReference
- 针对大量自旋问题 —— LongAdder
CAS
原理大致如下:
在java的 Unsafe 类里封装了一些 cas 的api。以 compareAndSetInt 为例,来看看其底层实现。
可以发现,最终会调用到 Atomic::cmpxchg 方法(Atomic::cmpxchg 在不同的操作系统中实现有所不同,上图所示是 linux_x86 的代码)。
Atomic::cmpxchg (exchange_value,dest, compare_value)
compare_value:是期望的当前值,如果 destination 的当前值等于 compare_value,则进行替换操作。
dest: 是要进行比较和替换的内存位置(通常是一个变量或内存地址)。
exchange_value: 是要设置到 destination 的新值。
返回:CAS成功返回替换值,CAS失败返回原值。
Atomic::cmpxchg 底层是 cmpxchgl 指令,该指令是一个硬件层面上的原子操作。除此之外如果是多核架构还加入lock前缀指令以实现内存屏障的效果。
cmpxchgl指令:首先比较 dest 指向的内存值是否和 compare_value 值相等,如果相等,则交换 dest 与 exchange_value,否则就单方面将 dest 指向的值赋给exchange_value。
Atomic 原子类
在理解了cas的底层实现后,再来看原子类。
一般原子类
一般的源自类无非就是直接调用了 Unsafe 中 cas 相关api。
以AtomicInteger为例:
而上面是在不考虑CAS自身缺陷情况下的一个封装,如果考虑上CAS缺陷,原子类该如何应对?
cas缺陷:1、aba问题 2、可能大量自旋
针对aba问题 —— AtomicStampedReference
试想这么一个场景:从读取内存值开始到执行cas原子指令之前,值被修改了两次,一次+1,一次-1。此时对于cas指令来说,内存中的值是不变的,就好像没被修改过一样,进而指令操作成功。这就是aba问题。
为解决aba问题,jdk增加了 AtomicStampedReference 类。核心设计思想是多加版本号的概念。当cas替换时不仅比对内存中的值是否是期望值,还会去比对版本号是否跟一开始的相等, 如果不相等,所以在这期间被修改过了,此时就cas失败。
除了 AtomicStampedReference 还有 AtomicMarkableReference 。区别在于 AtomicMarkableReference 并不关心修改了多少次,只关心是否被修改过。所以mark是一个boolean类型。
针对大量自旋问题 —— LongAdder
试想这么一个场景,假如同一时刻大量线程对库存数量-1,此时必然会有大量的自旋线程占用cpu资源。这明显是一个资源的浪费。如果不用cas,加锁的话,就会有线程切换,对性能必然会有所影响。有没有那么一种方案,既不加锁阻塞线程,又可以避免大量的自旋呢?
为解决这类场景的问题,jdk 1.8 引入了 LongAdder。
除 LongAdder 外还有 DoubleAdder、DoubleAccumulator、LongAccumulator,基本原理是一样的,本文以 LongAdder为例。
LongAccumulator相当于LongAdder的增强版。LongAdder只能针对数值的进行加减运算,而LongAccumulator提供了自定义的函数操作。
LongAdder的基本思路:
分散热点,将value值分散到数组中,不同线程会命中到数组的不同槽,每个线程只对自己槽中的值进行CAS,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只需将各个槽中的变量值累加返回即可。
重要变量:base变量 + Cells[]数组
base变量:非竞态条件下,直接累加到该变量上
Cells[]数组:竞态条件下,累加个各个线程自己的槽Cell[i]中
接下来从源码看看 LongAdder 是如何累加的:
LongAdder#add
public void add(long x) {Cell[] cs; long b, v; int m; Cell c;if ((cs = cells) != null || !casBase(b = base, b + x)) {boolean uncontended = true;if (cs == null || (m = cs.length - 1) < 0 ||(c = cs[getProbe() & m]) == null ||!(uncontended = c.cas(v = c.value, v + x)))longAccumulate(x, null, uncontended);}}
总之就是当 cells 还为初始化就直接cas base变量,如果一开始cells就已经初始化,或者cas base 变量失败就cas cells数组,如果cas cells数组也失败就进入Striped64#longAccumulate。
Striped64#longAccumulate
final void longAccumulate(long x, LongBinaryOperator fn,boolean wasUncontended) {//...//自旋循环done: for (;;) {Cell[] cs; Cell c; int n; long v;//如果cells不为空if ((cs = cells) != null && (n = cs.length) > 0) {//映射的槽位为空,新建并设置槽位if ((c = cs[(n - 1) & h]) == null) {if (cellsBusy == 0) {Cell r = new Cell(x);if (cellsBusy == 0 && casCellsBusy()) {try {Cell[] rs; int m, j;if ((rs = cells) != null &&(m = rs.length) > 0 &&rs[j = (m - 1) & h] == null) {rs[j] = r;break done;}} finally {cellsBusy = 0;}continue;}}collide = false;}//...(自旋重试的标记位处理)//不为空就 cas cell 槽位else if (c.cas(v = c.value, (fn == null) ? v + x : fn.applyAsLong(v, x)))break;//...(自旋重试的标记位处理)//cas cell 槽位失败尝试扩容cellselse if (cellsBusy == 0 && casCellsBusy()) {try {if (cells == cs) // Expand table unless stalecells = Arrays.copyOf(cs, n << 1);} finally {cellsBusy = 0;}collide = false;continue; // Retry with expanded table}h = advanceProbe(h);}//如果为空就新建 cells 并设置 cell 槽位else if (cellsBusy == 0 && cells == cs && casCellsBusy()) {try {if (cells == cs) {Cell[] rs = new Cell[2];rs[h & 1] = new Cell(x);cells = rs;break done;}} finally {cellsBusy = 0;}}//如果正在扩容,直接cas baseelse if (casBase(v = base,(fn == null) ? v + x : fn.applyAsLong(v, x)))break done;}}
总之就是如果cells还未初始化就初始化并设置此次槽位。如果正在初始化就直接cas base。如果已经初始化,就映射槽位,如果槽位为null,就创建槽位并设值,如果槽位不为null,就cas该槽位,如果槽位cas失败,就尝试扩容(cells大小 < cpu核数 时扩容)。
相关文章:

Java多线程篇(5)——cas和atomic原子类
文章目录 CASAtomic 原子类一般原子类针对aba问题 —— AtomicStampedReference针对大量自旋问题 —— LongAdder CAS 原理大致如下: 在java的 Unsafe 类里封装了一些 cas 的api。以 compareAndSetInt 为例,来看看其底层实现。 可以发现,最…...

数据结构---栈和队列
栈(Stack) 栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈 顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈࿱…...

2023-9-23 合并果子
题目链接:合并果子 #include <iostream> #include <algorithm> #include <queue>using namespace std;int main() {int n;cin >> n;priority_queue<int, vector<int>, greater<int>> heap;for(int i 0; i < n; i){in…...
基于QT和UDP实现一个实时RTP数据包的接收,并将数据包转化成文件
简单介绍:代码写的比较详细,需要留意的地方看结尾介绍 头文件 #ifndef RTPRECEIVER_H #define RTPRECEIVER_H#include <QDialog> #include <QUdpSocket> #include <QFile> #include <QTextStream> #include <httpclient.h&g…...

云原生安全性:保护现代应用免受威胁
文章目录 引言云原生安全性的挑战云原生安全性的关键实践1. 安全的镜像构建2. 网络策略3. 漏洞扫描和漏洞管理4. 认证和授权5. 日志和监控 云原生安全工具结论 🎉欢迎来到云计算技术应用专栏~云原生安全性:保护现代应用免受威胁 ☆* o(≧▽≦)o *☆嗨~我…...

R语言绘图-3-Circular-barplot图
0. 参考: https://r-graph-gallery.com/web-circular-barplot-with-R-and-ggplot2.html 1. 说明: 利用 ggplot 绘制 环状的条形图 (circular barplot),并且每个条带按照数值大小进行排列。 2 绘图代码: 注意:绘图代码中的字体…...

解决Keil5下载没有对应芯片Flash的问题
问题描述 例如芯片是STM32F103ZET6,但是选项中并没有对应型号的芯片导致下载失败。 解决方法 1、寻找芯片安装包的具体位置,芯片安装包路径在软件安装过程中会有(如图1所示)。如果没有记录可以双击一下芯片安装包会直接提示。…...

深拷贝与浅拷贝(对象的引用)
可以用赋值 1.对象的引用 代码: <!-- 1.对象的引用 --><script>const info{name:"lucy",age:20}const objinfo;info.name"sam"console.log(obj.name) //sam</script>图解: 等于号的赋值,对象info…...

重新认识架构—不只是软件设计
前言 什么是架构? 通常情况下,人们对架构的认知仅限于在软件工程中的定义:架构主要指软件系统的结构设计,比如常见的SOLID准则、DDD架构。一个良好的软件架构可以帮助团队更有效地进行软件开发,降低维护成本࿰…...
我的创业笔记:困境与思索
现在是2023年9月22日傍晚,我一个人走在广州的珠江边,静静地思索着当前个人创业面临的困境,不由自主地想将这些想法记录下来。 故事需要从两个月前说起。2023年7月31号,我从金山办公离职后,就满心欢喜地开启了自己的个…...

minio文件上传
1.代码 大佬仓库:https://gitee.com/Gary2016/minio-upload?_fromgitee_search 关于这个代码的讲解:来自b站 2.准备minio 参考:[1]、[2] 2.1 下载 官网:https://min.io/download#/windows 2.2 启动 ①准备一个data文件夹…...
IDEA .iml文件及.idea文件夹详解
.iml文件 idea 对module 配置信息之意, infomation of module。每个模块都有一个iml文件。 IDEA中的.iml文件是项目标识文件,缺少了这个文件,IDEA就无法识别项目。跟Eclipse的.project文件性质是一样的。并且这些文件不同的设备上的内容也会…...

使用Python做一个微信机器人
介绍 简介 该程序将微信的内部功能提取出来,然后在程序里加载Python,接着将这些功能导出成库函数,就可以在Python里使用这些函数 程序启动的时候会执行py_code目录下的main.py,类似于你在命令行使用python main.py。 现在会以…...

云计算战略:选择适合你业务的云平台
文章目录 云计算的概述选择云平台的关键因素1. 业务需求2. 预算3. 性能要求4. 数据隐私和合规性 示例:选择适合的云平台业务需求预算性能要求数据隐私和合规性 代码示例:使用云平台服务结论 🎉欢迎来到云计算技术应用专栏~云计算战略…...
Python:打印目录下每层的文件总数
代码如下: import osclass FileCount(object):def __init__(self,root_path: str):self.root_path root_pathself._count Noneself._file_count Noneself.children []def get_count(self):if self._count is None:self._count 0self._file_count 0for child_…...

LVS-NAT模式
LVS负载均衡群集 群集的定义 Cluster,集群(也称群集)由多台主机构成,但对外只表现为一一个整体,只提供一-个访问入口(域名或IP地址), 相当于一台大型计算机。 群集的作用 对于企业服务的的性能提升一般…...

【神印王座】龙皓晨竟然上了头版头条!内容违背,新闻真实性原则
Hello,小伙伴们,我是小郑继续为大家深度解析神印王座国漫。 大家有没有发现,当龙皓晨他们从驱魔关回到圣城时,有这么一幕,一个卖报小孩边走边说:驱魔关大捷,少年英雄龙皓晨操控守护与怜悯之神印王座&#x…...

C++之类和函数权限访问总结(二百二十七)
简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…...

手动部署 OceanBase 集群
手动部署一个 OB 单副本集群,包括一个 OBProxy 节点 部署环境 服务器信息 IP地址 192.168.0.26 网卡名 ifcfg-enp1s0 OS Kylin Linux Advanced Server release V10 CPU 8C 内存 32G 磁盘1 本地盘 /data/1 磁盘2 本地盘 /data/log1 机器和角色划分 …...

【操作系统笔记十二】Linux常用基础命令
Linux 常用快捷键 Tab 命令或路径等的补全键,特别常用的快捷键Ctrl insert 复制命令行内容(常用可提高效率)Shift insert 粘贴命令行内容(常用可提高效率)Ctrl C 中断当前任务(退出)Ctrl Z…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)
0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述,后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作,其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...