当前位置: 首页 > 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度…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动

一、前言说明 在2011版本的gb28181协议中&#xff0c;拉取视频流只要求udp方式&#xff0c;从2016开始要求新增支持tcp被动和tcp主动两种方式&#xff0c;udp理论上会丢包的&#xff0c;所以实际使用过程可能会出现画面花屏的情况&#xff0c;而tcp肯定不丢包&#xff0c;起码…...

Appium+python自动化(十六)- ADB命令

简介 Android 调试桥(adb)是多种用途的工具&#xff0c;该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具&#xff0c;其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利&#xff0c;如安装和调试…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...