无锁并发:探秘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问题。

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

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/56940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.6 TCP面向字节流

TCP 是面向字节流的协议&#xff0c;UDP 是面向报文的协议 操作系统对 TCP 和 UDP 协议的发送方的机制不同&#xff0c;也就是问题原因在发送方。 UDP面向报文协议&#xff1a; 操作系统不会对UDP协议传输的消息进行拆分&#xff0c;在组装好UDP头部后就交给网络层处理&…

Flask狼书笔记 | 03_模板

文章目录 3 模板3.1 模板基本使用3.2 模板结构组织3.3 模板进阶 3 模板 模板&#xff08;template&#xff09;&#xff1a;包含固定内容和动态部分的可重用文件。Jinja2模板引擎可用于任何纯文本文件。 3.1 模板基本使用 HTML实体&#xff1a;https://dev.w3.org/html5/htm…

kafka学习笔记

1、kafka是什么&#xff1f; kafka是一个高吞吐&#xff0c;分布式&#xff0c;基于发布/订阅的消息系统&#xff0c;最大的特性就是可以实时的处理大量的数据以满足各种需求场景&#xff1a;日志收集&#xff0c;离线和在线的消息消费&#xff0c;等等 2、kakfa的基础架构&am…

算法竞赛入门【码蹄集新手村600题】(MT1220-1240)C语言

算法竞赛入门【码蹄集新手村600题】(MT1220-1240&#xff09;C语言 目录MT1221 分数的总和MT1222 等差数列MT1223 N是什么MT1224 棋盘MT1225 复杂分数MT1226 解不等式MT1227 宝宝爬楼梯MT1228 宝宝抢糖果MT1229 搬家公司MT1230 圆周率MT1231圆周率IIMT1232 数字和MT1233 数字之…

c++的分文件编写

前言 在C中&#xff0c;你可以将代码分割成多个文件来提高可维护性和组织性。分割文件有助于将代码模块化&#xff0c;使大型项目更易于管理。以下是C中关于分文件的一些规则和概念&#xff1a; 理论知识 头文件&#xff08;Header Files&#xff09;&#xff1a; 头文件通常…

聚类分析 | MATLAB实现基于DBSCAD密度聚类算法可视化

聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化 目录 聚类分析 | MATLAB实现基于LP拉普拉斯映射的聚类可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 基于DBSCAD密度聚类算法可视化&#xff0c;MATLAB程序。 使用带有KD树加速的dbscan_with_kdtree函数进行…

Gateway简述

前言 ​ 在微服务架构中&#xff0c;一个系统会被拆分为很多个微服务。那么作为客户端调用多个微服务接口的地址。另外微服务架构的请求中&#xff0c;90%的都携带认证信息/用户登录信息&#xff0c;都需要做相关的限制管理&#xff0c;API网关由此应允而生。 这样的架构会存…

DevOps系列文章之 Python基础

列表 Python中的列表类似于C语言中的数组的概念&#xff0c;列表由内部的元素组成&#xff0c;元素可以是任何对象 Python中的列表是可变的 简单的理解就是&#xff1a;被初始化的列表&#xff0c;可以通过列表的API接口对列表的元素进行增删改查 1、定义列表 1.可以将列表当成…

《2023年网信人才培训-网络安全从业人员能力素养提升培训》第一期成功举办

随着网络强国和数字中国建设的步伐加快&#xff0c;建设规模宏大、结构合理、素质优良的人才队伍成为一项重要工作。知了汇智作为数字产教融合基地&#xff0c;通过与高校、企业等多方合作&#xff0c;建立了完整的网络安全人才培养生态链。凭借自身技术优势和丰富的产业资源&a…

m4s格式转换mp4

先安装 ffmpeg&#xff0c;具体从官网可以查到&#xff0c;https://ffmpeg.org&#xff0c;按流程走。 转换代码如下&#xff0c;可以任意选择格式导出 import subprocess import osdef merge_audio_video(input_audio_path, input_video_path, output_mp4_path):# 构建 FFmpe…

呈现数据的精妙之道:选择合适的可视化方法

在当今数据时代&#xff0c;数据可视化已成为理解和传达信息的重要手段。然而&#xff0c;选择适合的数据可视化方法对于有效地呈现数据至关重要。不同的数据和目标需要不同的可视化方法&#xff0c;下面我们将探讨如何选择最佳的数据可视化方法来呈现数据。 1. 理解数据类型&a…

在线OJ平台项目

一、项目源码 Online_Judge yblhlk/Linux课程 - 码云 - 开源中国 (gitee.com) 二、所用技术与开发环境 1.所用技术: MVC架构模式 (模型&#xff0d;视图&#xff0d;控制器) 负载均衡系统设计 多进程、多线程编程 C面向对象编程 & C 11 & STL 标准库 C Boost 准标…

【前端】CSS技巧与样式优化

目录 一、前言二、精灵图1、什么是精灵图2、为什么需要精灵图3、精灵图的使用①、创建CSS精灵图的步骤1&#xff09;、选择合适的图标2&#xff09;、合并图片3&#xff09;、设置背景定位 ②、优化CSS精灵图的技巧1&#xff09;、维护方便2&#xff09;、考虑Retina屏幕3&…

业务系统架构实践总结

我从2015年起至今2022年&#xff0c;在业务平台&#xff08;结算、订购、资金&#xff09;、集团财务平台&#xff08;应收应付、账务核算、财资、财务分析、预算&#xff09;、本地生活财务平台&#xff08;发票、结算、预算、核算、稽核&#xff09;所经历的业务系统研发实践…

18V降压5V芯片

航誉微高效率同步降压芯片。输出电流可以高达2A。采用两种工作模式&#xff1a;PWM与PFM切换工作。92%的占空比实现了低压操作并延长了便携系统的电池使用寿命&#xff1b;输出电压可调&#xff1b;振荡频率为 600KHz&#xff08;典型值&#xff09;。内部同步开关提高了效率并…

D.OASIS City 和 Warrix 在The Sandbox 庆祝 Rise of the 10th Legend十周年

D.OASIS 首次展示了变革性娱乐 D.OASIS City&#xff0c;正如它与 WARRIX 一起承诺的那样。WARRIX 是获得泰国国家队球衣生产授权的标志性运动服装品牌。 这款激动人心的游戏冒险游戏于今天推出&#xff0c;让用户能够投入 D.OASIS City x WARRIX&#xff1a;Rise of the 10th…

一文速学-让神经网络不再神秘,一天速学神经网络基础-激活函数(二)

前言 思索了很久到底要不要出深度学习内容&#xff0c;毕竟在数学建模专栏里边的机器学习内容还有一大半算法没有更新&#xff0c;很多坑都没有填满&#xff0c;而且现在深度学习的文章和学习课程都十分的多&#xff0c;我考虑了很久决定还是得出神经网络系列文章&#xff0c;…

健康安全的新定义,照明舒适达到巅峰,SUKER书客护眼台灯L1震撼发售

深耕照明领域多年的SUKER书客&#xff0c;这一次给大家带来一份大惊喜。在最近正式发布新品——SUKER书客护眼台灯L1&#xff0c;这款护眼台灯承载着在照明领域的前沿技术&#xff0c;能保证照明安全健康和舒适度并带来非常优秀的护眼效果。作为书客在护眼台灯领域的颠覆式新品…

[JavaWeb]【九】web后端开发-SpringBootWeb案例(菜单)

目录 一、准备工作 1.1 需求 1.2 环境搭建 1.2.1 准备数据库&表 1.2.2 创建springboot工程 1.2.3 配置application.properties & 准备对应实体类 1.2.3.1 application.properties 1.2.3.2 实体类 1.2.3.2.1 Emp类 1.2.3.2.2 Dept类 1.2.4 准备对应的Mapper、…

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

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