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

目录
- 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机制的魔力
😊 作者: 一恍过去 💖 主页: https://blog.csdn.net/zhuocailing3390 🎊 社区: Java技术栈交流 🎉 主题: 无锁并发:探秘CAS机制的魔力 ⏱️ 创作时间: 2…...
iOS App签名与重签名:从开发者证书到重新安装运行
前文回顾: iOS脱壳技术(二):深入探讨dumpdecrypted工具的高级使用方法 iOS逆向:越狱及相关概念的介绍 在本文中,我们将详细介绍iOS应用的签名过程,包括开发者证书的种类、证书与App ID、Provisi…...
vue项目,如何修改Element-Plus等UI组件库的样式,三种方式搞定!!!
前言 我们在学习和使用组件库构建页面的时候,时常会遇到这样的问题。 即,尽管组件库已经提供了较多的功能,来帮助我们构建自定义的效果,但有时仍不能使我们满意。 这个时候我们就不得不修改UI库的样式,来达到想要的状…...
httpd协议与apache
1.http 相关概念 HTTP是处于应用层的协议,使用TCP传输层协议进行可靠的传送。因此,需要特别提醒的是,万维网是基于因特网的一种广泛因特网应用系统,且万维网采用的是HTTP(80/TCP)和 HTTPS(443/…...
Go 自学:文件的写入和读取
首先,使用os.Create()函数建立一个文件。 接着,使用io.WriteString()函数将内容写入文件。 最后,使用os.ReadFile()函数读取文件内容。 注意,这里读取的文件内容是data byte,我们需要使用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 程序放入后台运行, 同时将 stdout 输出到 log.log 文件中&…...
【git】would clobber existing tag 报错解决
问题 在用vscode的Git去pull代码的时候git弹窗报错,查看报错日志发现以下内容: > 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库将图片中的文章提取出来 初环境内容步骤一:安装easyocr库步骤二:导入必要的库步骤三:创建OCR阅读器对象步骤四:指定要识别的图片路径步骤五:执行OCR识别并提取文章内容步骤六:遍历…...
门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据
环境: ivms-4200 v3.10.0.6_c 问题描述: 门禁系统忘记登入密码,现在更换电脑如何迁移旧电脑门禁系统的数据,旧电脑记住密码,忘了密码和密保了 解决方案: 1.前往海康官网下载4200客户端,在新电脑上安装 …...
初试Eureka注册中心
Eureka是spring cloud中的一个负责服务注册与发现的组件。遵循着CAP理论中的A(可用性)P(分区容错性)。一个Eureka中分为eureka server和eureka client。其中eureka server是作为服务的注册与发现中心。 搭建eureka服务 引入eureka依赖 引入SpringCloud为eureka提供的starter依…...
【趣味随笔】怎么维护自己的电脑?
📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…...
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启动:指定配置文件 Springboot项目有如下配置文件 主配置文件application.yml, 测试环境:application-test.yml 生产环境:application-pro.yml 开发环境:application-dev.yml 1.1.配置文件…...
python可视化matplotlib——绘制正弦和余弦
这是一个使用matplotlib库绘制正弦和余弦函数曲线的代码示例。代码中导入了需要的库,并设置了x轴和y轴的标签字体为华文楷体。然后,使用numpy生成一组x轴上的值t,并使用正弦函数生成对应的y轴值s,再使用余弦函数生成对应的y轴值z。…...
Day48|leetcode 198.打家劫舍、213.打家劫舍II、打家劫舍|||
leetcode 198.打家劫舍 题目链接:198. 打家劫舍 - 力扣(LeetCode) 视频链接:动态规划,偷不偷这个房间呢?| LeetCode:198.打家劫舍_哔哩哔哩_bilibili 题目概述 你是一个专业的小偷,…...
Mysql001:Mysql概述以及安装
前言:本课程将从头学习Mysql,以我的工作经验来说,sql语句真的太重要的,现在互联网所有的一切都是建立在数据上,因为互联网的兴起,现在的数据日月增多,每年都以翻倍的形式增长,对于数…...
如何调用api接口获取到商品数据
要调用API接口获取商品数据,需要进行以下步骤: 1.确定API接口 首先需要确定要使用的API接口,可以通过搜索引擎或者相关文档来查找适合的API接口。以淘宝开放平台为例,可以使用淘宝的商品信息查询API接口来获取商品数据。 2.注册…...
http请求方式过滤器与拦截器的区别
get:获取查询数据(查询)post:数据的提交,新增操作(增加)put:向服务端发送数据、改变信息,侧重点在于对数据的修改操作delete:数据库数据的删除head:一般用来判断类型、根据返回状态确定资源是否存在、资源是否更新以及更新的时间等 过滤器与拦截器的区别…...
大语言模型初学者指南 (2023)
大语言模型 (LLM) 是深度学习的一个子集,它正在彻底改变自然语言处理领域。它们是功能强大的通用语言模型,可以针对大量数据进行预训练,然后针对特定任务进行微调。这使得LLM能够拥有大量的一般数据。如果一个人想将LLM用于特定目的ÿ…...
日常生活小技巧 -- 单位换算
开发过程中经常需要需要单位换算的地方。 可以使用工具进行转换: 工具:单位转换 常用单位: 1、角度转换 1弧度(rad) 180/PI 度(deg) 57.29577951308232 度(deg) 1度…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
HBuilderX安装(uni-app和小程序开发)
下载HBuilderX 访问官方网站:https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本: Windows版(推荐下载标准版) Windows系统安装步骤 运行安装程序: 双击下载的.exe安装文件 如果出现安全提示&…...
涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案
JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停 1. 安全点(Safepoint)阻塞 现象:JVM暂停但无GC日志,日志显示No GCs detected。原因:JVM等待所有线程进入安全点(如…...
