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

无锁并发:探秘CAS机制的魔力

😊 @ 作者: 一恍过去
💖 @ 主页: https://blog.csdn.net/zhuocailing3390
🎊 @ 社区: Java技术栈交流
🎉 @ 主题: 无锁并发:探秘CAS机制的魔力
⏱️ @ 创作时间: 2023年08月28日

在这里插入图片描述

目录

  • 1、概念
  • 2、原理
  • 3、缺点
  • 4、ABA问题
  • 5、解决ABA问题

1、概念

CAS(Compare And Swap): 比较并替换,它是一条CPU原语,是一条原子指令(原子性)。
CAS通过比较真实值与预期值是否相同,如果是则进行修改,Atomic原子类底层就是使用了CAS,CAS属于乐观锁。
CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么会自动将该内存位置的值更新为新值,反之不做任何操作。

2、原理

CAS执行依赖于Unsafe类,Unsafe类的所有方法通过native修饰,所以Unsafe类直接操作系统底层的数据地址,而不通过JVM实现。
比如:A、B线程通过AtomicInteger同时对变量进行自增

    public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);}// 使用了unsafe类public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {// 获取原值var5 = this.getIntVolatile(var1, var2);// 将原值与预期值进行对比,一致则进行相加var4} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}

伪代码流程解释:
A线程将变量num从1进行自增,当执行时没有其他线程修改过num值,所以一次成功,num自增后将值返回给主内存。
B线程也将num进行自增,但是num已经被A线程进行了修改,所有将再次执行do-while,重新获取num的值。
由于JMM模型的可见性,B线程重新获取值时会从主内存中获取到被A修改后的最新值。
通过最新值与期望值进行比较,满足条件就返回true。

CAS如何实现:
通过Unsafe类对系统底层的数据地址进行原子性操作,对比内存地址的值和预期值是否一样,如果一样进行修改。

3、缺点

  • 1、循环时间长,增加了CPU的开销。
  • 2、只能保证一个变量的原子操作。
  • 3、会导致ABA问题。

4、ABA问题

如线程1从内存X中取出A,这时候另一个线程2也从内存X中取出A,并且线程2进行了一些操作将内存X中的值变成了B,然后线程2又将内存X中的数据变成A,这时候线程1进行CAS操作发现内存X中仍然是A,然后线程1操作成功。虽然线程1的CAS操作成功,但是整个过程就是有问题的,因为内存X中的值从A到B再到了A。

CAS执行时,将过去某时刻的值与当下时刻进行比较并替换,在这时间差中,值可能会发生多次修改,只是最终值的结果不变。

5、解决ABA问题

为了防止在值比较时,存在被修改过的可能,通过为值加上版本号的方式,在最后执行CAS时判断版本号,确保不会出现ABA问题。

通过原子引用(AtomicReference)加时间戳原子引用(AtomicStampedReference)解决ABA问题。

代码如下:

public class Test {private static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);public static void main(String[] args) {System.out.println("=========以下ABA问题产生:=========");// 线程 t1 模拟ABA问题的产生new Thread(() -> {// 进行一次修改,将值修改为101atomicReference.compareAndSet(100, 101);// 进行第二次修改,将值修改回100atomicReference.compareAndSet(101, 100);}, "t1").start();new Thread(() -> {// 线程t2暂停1S,保证线程t1执行完成try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}System.out.println(atomicReference.compareAndSet(100, 102));System.out.println("修改成功,修改后值为:"+atomicReference.get());}, "t2").start();try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}System.out.println("=========以下ABA问题解决方式:=========");new Thread(() -> {// 获取版本号int stamp = atomicStampedReference.getStamp();System.out.println("线程名称:"+Thread.currentThread().getName()+",第一次版本号:"+stamp);try {TimeUnit.SECONDS.sleep(1);} catch (Exception e) {e.printStackTrace();}// 进行一次修改,将值修改为101atomicStampedReference.compareAndSet(100,101,stamp,stamp+1);stamp = atomicStampedReference.getStamp();System.out.println("线程名称:"+Thread.currentThread().getName()+",第二次版本号:"+stamp);// 进行第二次修改,将值修改回100atomicStampedReference.compareAndSet(101,100,stamp,stamp+1);stamp = atomicStampedReference.getStamp();System.out.println("线程名称:"+Thread.currentThread().getName()+",第三次版本号:"+stamp);}, "t3").start();new Thread(() -> {// 获取版本号int stamp = atomicStampedReference.getStamp();System.out.println("线程名称:"+Thread.currentThread().getName()+",第一次版本号:"+stamp);// 线程t4暂停1S,保证线程t3执行完成try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}boolean andSet = atomicStampedReference.compareAndSet(100, 101, stamp,stamp+1);System.out.println("线程名称:"+Thread.currentThread().getName()+",修改结果:"+andSet+",第二次版本号:"+stamp+1+"实际版本号:"+atomicStampedReference.getStamp());System.out.println("当前最新值:"+atomicStampedReference.getReference());}, "t4").start();}
}

分析:
线程t1中,将值从100修改为了101又修改回了100,但是线程t2却成功更新了值,所以产生了ABA问题。
线程t3中,将值从100修改为了101又修改回了100,并且更新的版本号为3;在线程t4中,将100修改为101时,由于线程t4更新时的版本号为2,但是实际的版本号为3,所以无法更新,解决了ABA问题。

运行结果:
在这里插入图片描述

在这里插入图片描述

相关文章:

无锁并发:探秘CAS机制的魔力

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; 无锁并发&#xff1a;探秘CAS机制的魔力 ⏱️ 创作时间&#xff1a; 2…...

iOS App签名与重签名:从开发者证书到重新安装运行

前文回顾&#xff1a; iOS脱壳技术&#xff08;二&#xff09;&#xff1a;深入探讨dumpdecrypted工具的高级使用方法 iOS逆向&#xff1a;越狱及相关概念的介绍 在本文中&#xff0c;我们将详细介绍iOS应用的签名过程&#xff0c;包括开发者证书的种类、证书与App ID、Provisi…...

vue项目,如何修改Element-Plus等UI组件库的样式,三种方式搞定!!!

前言 我们在学习和使用组件库构建页面的时候&#xff0c;时常会遇到这样的问题。 即&#xff0c;尽管组件库已经提供了较多的功能&#xff0c;来帮助我们构建自定义的效果&#xff0c;但有时仍不能使我们满意。 这个时候我们就不得不修改UI库的样式&#xff0c;来达到想要的状…...

httpd协议与apache

1.http 相关概念 HTTP是处于应用层的协议&#xff0c;使用TCP传输层协议进行可靠的传送。因此&#xff0c;需要特别提醒的是&#xff0c;万维网是基于因特网的一种广泛因特网应用系统&#xff0c;且万维网采用的是HTTP&#xff08;80/TCP&#xff09;和 HTTPS&#xff08;443/…...

Go 自学:文件的写入和读取

首先&#xff0c;使用os.Create()函数建立一个文件。 接着&#xff0c;使用io.WriteString()函数将内容写入文件。 最后&#xff0c;使用os.ReadFile()函数读取文件内容。 注意&#xff0c;这里读取的文件内容是data byte&#xff0c;我们需要使用string()函数将其转换为字符串…...

py 项目上线centos

1 服务器py版本 ps -ef|grep python|grep -v grep 2 2.x版本 安装 PyMySQL pip install PyMySQL0.9.3 3 后台运行py文件 nohup python down.py 1 > log.log 2>&1 & 这个命令将 down.py 程序放入后台运行&#xff0c; 同时将 stdout 输出到 log.log 文件中&…...

【git】would clobber existing tag 报错解决

问题 在用vscode的Git去pull代码的时候git弹窗报错&#xff0c;查看报错日志发现以下内容&#xff1a; > git pull --tags origin feature/xxx-2.0.0 From 173.110.11.22:VV-WORK-FE/vv-desktop* branch feature/xxx-2.0.0 -> FETCH_HEAD! [rejected] …...

Python OCR 使用easyocr库将图片中的文章提取出来

Python OCR 使用easyocr库将图片中的文章提取出来 初环境内容步骤一&#xff1a;安装easyocr库步骤二&#xff1a;导入必要的库步骤三&#xff1a;创建OCR阅读器对象步骤四&#xff1a;指定要识别的图片路径步骤五&#xff1a;执行OCR识别并提取文章内容步骤六&#xff1a;遍历…...

门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据

环境&#xff1a; ivms-4200 v3.10.0.6_c 问题描述&#xff1a; 门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据&#xff0c;旧电脑记住密码&#xff0c;忘了密码和密保了 解决方案&#xff1a; 1.前往海康官网下载4200客户端&#xff0c;在新电脑上安装 …...

初试Eureka注册中心

Eureka是spring cloud中的一个负责服务注册与发现的组件。遵循着CAP理论中的A(可用性)P(分区容错性)。一个Eureka中分为eureka server和eureka client。其中eureka server是作为服务的注册与发现中心。 搭建eureka服务 引入eureka依赖 引入SpringCloud为eureka提供的starter依…...

【趣味随笔】怎么维护自己的电脑?

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…...

element 下拉组件获取对象

// 选择数据user:[{name:"小白",id:1,money:"100",love:"蛋糕"},{name:"小黑",id:2,money:"200",love:"奶茶"},{name:"小红",id:3,money:"300",love:"烧烤"},] <div><el…...

IDEA下SpringBoot指定环境、配置文件启动

1、idea下的SpringBoot启动&#xff1a;指定配置文件 Springboot项目有如下配置文件 主配置文件application.yml&#xff0c; 测试环境&#xff1a;application-test.yml 生产环境&#xff1a;application-pro.yml 开发环境&#xff1a;application-dev.yml 1.1.配置文件…...

python可视化matplotlib——绘制正弦和余弦

这是一个使用matplotlib库绘制正弦和余弦函数曲线的代码示例。代码中导入了需要的库&#xff0c;并设置了x轴和y轴的标签字体为华文楷体。然后&#xff0c;使用numpy生成一组x轴上的值t&#xff0c;并使用正弦函数生成对应的y轴值s&#xff0c;再使用余弦函数生成对应的y轴值z。…...

Day48|leetcode 198.打家劫舍、213.打家劫舍II、打家劫舍|||

leetcode 198.打家劫舍 题目链接&#xff1a;198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 视频链接&#xff1a;动态规划&#xff0c;偷不偷这个房间呢&#xff1f;| LeetCode&#xff1a;198.打家劫舍_哔哩哔哩_bilibili 题目概述 你是一个专业的小偷&#xff0c;…...

Mysql001:Mysql概述以及安装

前言&#xff1a;本课程将从头学习Mysql&#xff0c;以我的工作经验来说&#xff0c;sql语句真的太重要的&#xff0c;现在互联网所有的一切都是建立在数据上&#xff0c;因为互联网的兴起&#xff0c;现在的数据日月增多&#xff0c;每年都以翻倍的形式增长&#xff0c;对于数…...

如何调用api接口获取到商品数据

要调用API接口获取商品数据&#xff0c;需要进行以下步骤&#xff1a; 1.确定API接口 首先需要确定要使用的API接口&#xff0c;可以通过搜索引擎或者相关文档来查找适合的API接口。以淘宝开放平台为例&#xff0c;可以使用淘宝的商品信息查询API接口来获取商品数据。 2.注册…...

http请求方式过滤器与拦截器的区别

get:获取查询数据(查询)post:数据的提交&#xff0c;新增操作(增加)put:向服务端发送数据、改变信息&#xff0c;侧重点在于对数据的修改操作delete:数据库数据的删除head:一般用来判断类型、根据返回状态确定资源是否存在、资源是否更新以及更新的时间等 过滤器与拦截器的区别…...

大语言模型初学者指南 (2023)

大语言模型 (LLM) 是深度学习的一个子集&#xff0c;它正在彻底改变自然语言处理领域。它们是功能强大的通用语言模型&#xff0c;可以针对大量数据进行预训练&#xff0c;然后针对特定任务进行微调。这使得LLM能够拥有大量的一般数据。如果一个人想将LLM用于特定目的&#xff…...

日常生活小技巧 -- 单位换算

开发过程中经常需要需要单位换算的地方。 可以使用工具进行转换&#xff1a; 工具&#xff1a;单位转换 常用单位&#xff1a; 1、角度转换 1弧度&#xff08;rad&#xff09; 180/PI 度&#xff08;deg&#xff09; 57.29577951308232 度&#xff08;deg&#xff09; 1度…...

告别第三方工具:手把手教你打造微软官方WinPE系统维护盘

1. 为什么你需要一个官方WinPE维护盘&#xff1f; 每次电脑系统崩溃时&#xff0c;你是不是也在各大论坛疯狂搜索"如何重装系统"&#xff1f;市面上确实有很多第三方PE工具&#xff0c;比如老毛桃、微PE之类的&#xff0c;用起来确实方便。但作为一个在IT行业摸爬滚…...

QRazyBox终极指南:5步快速修复损坏的二维码

QRazyBox终极指南&#xff1a;5步快速修复损坏的二维码 【免费下载链接】qrazybox QR Code Analysis and Recovery Toolkit 项目地址: https://gitcode.com/gh_mirrors/qr/qrazybox 你是否曾经遇到过无法扫描的二维码&#xff1f;那些因打印模糊、屏幕损坏或污渍覆盖而失…...

抖音无水印下载终极指南:douyin-downloader 快速入门与高效使用

抖音无水印下载终极指南&#xff1a;douyin-downloader 快速入门与高效使用 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallb…...

从FindBugs到SpotBugs:在IntelliJ IDEA中升级你的代码静态分析工具链(避坑指南)

从FindBugs到SpotBugs&#xff1a;在IntelliJ IDEA中升级你的代码静态分析工具链&#xff08;避坑指南&#xff09; 如果你是一位长期使用FindBugs进行Java代码静态分析的中高级开发者&#xff0c;最近可能在IntelliJ IDEA的新版本中遇到了插件安装失败的问题。这不是你的操作失…...

多智能体编排实战:从架构设计到生产部署的12周训练指南

1. 项目概述与核心价值最近在探索如何系统性地掌握多智能体编排技术时&#xff0c;我遇到了一个名为“Shadow Dojo”的开源项目。这个名字很有意思&#xff0c;“道场”一词本身就意味着一个需要持续练习、精进技艺的地方。这个项目将自己定位为“训练场”&#xff0c;目标非常…...

家庭稳定性的具象化的庖丁解牛

它的本质是&#xff1a;家庭不是一个静态的物体&#xff0c;而是一个 动态平衡的复杂自适应系统 (Complex Adaptive System)。其稳定性不取决于“没有冲突”&#xff0c;而取决于系统在遭遇外部冲击&#xff08;失业、疾病、经济下行&#xff09;和内部扰动&#xff08;争吵、代…...

网盘下载提速终极指南:9大平台直链获取工具完整教程

网盘下载提速终极指南&#xff1a;9大平台直链获取工具完整教程 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 &#xff0c;支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云…...

AI报告审核正在提升阻燃材料检测可信度:IACheck如何减少PSU阻燃等级报告里的合规风险

做高性能工程塑料检测的人都知道&#xff0c;PSU材料的阻燃等级测试&#xff0c;看起来只是一个等级判定&#xff0c;但真正进入报告审核阶段以后&#xff0c;往往比实验本身更容易出问题。因为PSU&#xff0c;也就是聚砜材料&#xff0c;常用于电子电气、轨道交通、医疗器械以…...

自主AI助手Angel:基于大语言模型的多通道智能体部署与实战

1. 项目概述&#xff1a;一个能帮你“跑腿”的自主AI助手如果你和我一样&#xff0c;经常在Discord、Slack、iMessage和Signal这几个聊天软件之间来回切换&#xff0c;同时电脑上还开着十几个终端窗口处理各种杂事&#xff0c;那你肯定幻想过能有一个“数字管家”。它最好能看懂…...

3步掌握Blender 3MF插件:构建高效3D打印工作流

3步掌握Blender 3MF插件&#xff1a;构建高效3D打印工作流 【免费下载链接】Blender3mfFormat Blender add-on to import/export 3MF files 项目地址: https://gitcode.com/gh_mirrors/bl/Blender3mfFormat 在3D打印和数字制造领域&#xff0c;模型格式转换是连接设计与…...