【JavaEE】CAS(Compare And Swap)操作
文章目录
- 什么是 CAS
- CAS 的应用
- 如何使用 CAS 操作实现自旋锁
- CAS 的 ABA 问题
- CAS 相关面试题
什么是 CAS
CAS(Compare and Swap)是一种原子操作,用于在无锁情况下保证数据一致性的问题。它包含三个操作数——内存位置、预期原值及更新值。在执行CAS操作时,会将内存位置的值与预期原值进行比较。如果两者相等,则处理器会自动将该位置的值更新为新值;如果不相等,则处理器不做任何操作。这个过程是原子的,即在整个操作期间,不会被其他线程或进程中断。
在多线程并发编程中,CAS操作可以避免传统的锁机制引起的线程阻塞和上下文切换等问题,提高程序的并发性能。
CAS伪代码
boolean CAS(address, expectValue, swapValue) {//如果内存address中的值和expectValue相等话,//就将swapValue的值赋给adress,并且返回trueif (&address == expectValue) {&address = swapValue;return true;}return false;
}
CAS 是一个 CPU 指令,具有原子性,而具有原子性的操作就代表着不需要加锁就可以保证线程的安全,所以 CAS 操作就可以替代某些加锁的操作。
CAS 本质上是 CPU 提供的指令,然后被操作系统封装形成 API 后,又被 JVM 或者其它封装成为 API 之后,我们程序员才可以直接使用 CAS 的相关操作。
CAS 的应用
CAS 经过 CPU 和 JVM 封装之后,我们在 Java 代码中就可以直接使用 CAS 操作,那么我们来看看在 Java 中如何使用 CAS 操作。
标准库中提供了 java.util.concurrent.atomic
包, 里面的类都是基于这种方式来实现的.
我们可以根据需要创建出合适的类,如果你要进行 CAS 的数据类型为 int 类型的话,就创建 AtomicInteger 类,如果是 boolean 类型的话,就创建出 AtomicBoolean 类型。
AtomicInteger 类中有很多方法,但是我们今天主要了解 getAndDecrement
方法和 getAndIncrement
方法,它们分别表示–和++操作。
public class Test {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInteger = new AtomicInteger(0);Thread t1 = new Thread(() -> {for(int i = 0; i < 1000; i++) {atomicInteger.getAndIncrement();}});Thread t2 = new Thread(() -> {for(int i = 0; i < 1000; i++) {atomicInteger.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(atomicInteger.get()); //2000}
}
public class Test {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInteger = new AtomicInteger(10000);Thread t1 = new Thread(() -> {for(int i = 0; i < 1000; i++) {atomicInteger.getAndDecrement();}});Thread t2 = new Thread(() -> {for(int i = 0; i < 1000; i++) {atomicInteger.getAndDecrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(atomicInteger.get()); //8000}
}
++或者–的操作不具有原子性,如果在多线程中进行++或者–操作的时候往往会发生线程不安全问题,导致最终的结果不是我们想要的结果,而这里我们使用 CAS 操作的话就保证了++和–操作的原子性,并且也避免了加锁阻塞的现象,既保证了答案的正确性,又保证了运行速度。
查看 getAndIncrement 方法我们可以看到这个方法里面又调用了 getAndAddInt 方法,但是这个方法是属于 unsafe 的,unsafe 中的方法都是偏底层且操作较危险的操作。
可以看到 getAndAddInt 方法中是没有加锁操作的。
compareAndSwapInt 方法是 native 修饰的本地方法,这个方法是 JVM 底层由 C/C++ 写的,我们是看不到的。
这里 getAndIncremnet 方法还是用伪代码来实现一遍。
class AtomicInteger {private int value;public int getAndIncrement() {//这里现在寄存器当中存储value的值int oldValue = value;//比较内存中的value值是否和寄存器当中的oldValue相同//如果相同,则说明该过程中value的值没有被修改,然后将后面的修改值赋给value//如果不相同,说明在这个过程中value的值被修改了,那么更新oldValue的值while (CAS(value, oldValue, oldValue + 1) != true)oldValue = value;}return oldValue;}
}
如何使用 CAS 操作实现自旋锁
前面【JavaEE】锁策略中为大家讲解了什么是自旋锁,自旋锁就是当线程想要获取到锁,但是这个锁正别其他线程使用的时候,一般请情况下线程会进入阻塞等待状态,但是自旋锁不是,它不释放 CPU 资源,反复确认这个锁是否被释放,使得整个操作一直处于用户态操作,减少了内核态操作而增加一些其他操作。接下来,我们就来使用 CAS 操作来实现自旋锁。
public class SpinLock {//owner表示当前锁是被哪个线程所拥有,当owner为null的时候表示该所可以被获取private Thread owner = null;//while判断当前owner时候为null,如果是,则获取到整个锁,修改owner为当前线程//如果owner不为null,则表示锁被其他线程使用,那么就会返回false,while里面的//判断就为true,进入死循环,直到其他线程使用unlock方法释放锁public void lock() {while (!CAS(this.owner, null, Thread.currentThread())) {}}public void unlock() {this.owner = null;}
}
通过 CAS 操作就解决了当多个线程竞争一个锁的时候,线程进入阻塞等待状态由用户态操作转为内核态的情况,保证了程序处于用户态的操作状态。
CAS 的 ABA 问题
CAS 操作是判断内存中的数据是否和寄存器中的值相等,那么是否会发生一种情况就是:在这个过程中内存中的数据由 A -> B -> A,也就是说内存中的数据被修改了一次,但是最后又被改回来了的情况呢?当然是可能的,那么如果发生这种情况的时候是否会出现问题呢?
使用 CAS 操作的时候,如果发生 ABA 的问题时,一般不会出现问题,但是有些特殊的情况会造成问题。比如:我现在是大学生,每个月我的父母就会向我的银行卡里面打钱,我呢手机绑定了银行卡,就需要从银行卡中将这些钱充值到微信或者支付宝上,我打算充值1000块,但是当我点击充值按钮的时候,因为网卡,我点了一次没反应,所以我又点了一次,当网络好了的时候,它后台就显示我点击了两次,但是实际上我只想充值一次,那么微信或者支付宝的后台就会有两个线程执行 CAS 操作。
但是如果在这个时候,我的父母又给我银行卡里面打了1000块钱的时候会发生什么呢?
那么如何解决 CAS 的 ABA 问题呢?造成 ABA 的问题就是变量既有增加也有减少,如果我们使用的变量是只增或者只减的话,那么就不会发生这种 ABA 问题。我们可以引入一个额外的变量:版本号,换个版本号是只增的,修改一次余额就增加版本号一次,当执行 CAS 操作的时候会判断内存中的版本号和寄存器当中的版本号是否相同,相同则可以执行,不相同就说明中间穿插了其他的修改操作,不执行修改操作。
public class Test2 {private int value;//number表示版本号private int number = 0;public void add(int money) {int oldNumber = number;//进行一次修改操作之后版本号就加1if (CAS(number, oldNumber, oldNumber + 1)) {value += money;}}
}
CAS 相关面试题
1) 讲解下你自己理解的 CAS 机制
全称 Compare and swap, 即 “比较并交换”. 相当于通过一个原子的操作, 同时完成 “读取内存, 比较是否相等, 修改内存” 这三个步骤. 本质上需要 CPU 指令的支撑
2) ABA问题怎么解决?
给要修改的数据引入版本号. 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期.如果发现当前版本号和之前读到的版本号一致, 就真正执行修改操作, 并让版本号自增; 如果发现当前版本号比之前读到的版本号大, 就认为操作失败
相关文章:

【JavaEE】CAS(Compare And Swap)操作
文章目录 什么是 CASCAS 的应用如何使用 CAS 操作实现自旋锁CAS 的 ABA 问题CAS 相关面试题 什么是 CAS CAS(Compare and Swap)是一种原子操作,用于在无锁情况下保证数据一致性的问题。它包含三个操作数——内存位置、预期原值及更新值。在执…...
第三章:最新版零基础学习 PYTHON 教程(第三节 - Python 运算符—Python 中的关系运算符)
关系运算符用于比较值。它根据条件返回 True 或 False。这些运算符也称为比较运算符。 操作员描述 句法> 大于:如果左操作数大于右操作数,则为 Truex > y...

【GDB】使用 GDB 自动画红黑树
阅读本文前需要的基础知识 用 python 扩展 gdb python 绘制 graphviz 使用 GDB 画红黑树 前面几节中介绍了 gdb 的 python 扩展,参考 用 python 扩展 gdb 并且 python 有 graphviz 模块,那么可以用 gdb 调用 python,在 python 中使用 grap…...

使用Vue3+elementPlus的Tree组件实现一个拖拽文件夹管理
文章目录 1、前言2、分析3、实现4、踩坑4.1、拖拽辅助线的坑4.2、数据的坑4.3、限制拖拽4.4、样式调整 1、前言 最近在做一个文件夹管理的功能,要实现一个树状的文件夹面板。里面包含两种元素,文件夹以及文件。交互要求如下: 创建、删除&am…...

小谈设计模式(7)—装饰模式
小谈设计模式(7)—装饰模式 专栏介绍专栏地址专栏介绍 装饰模式装饰模式角色Component(抽象组件)ConcreteComponent(具体组件)Decorator(抽象装饰器)ConcreteDecorator(具…...

nginx 多层代理 + k8s ingress 后端服务获取客户真实ip 配置
1.nginx http 七层代理 修改命令空间: namespace: nginx-ingress : configmap:nginx-configuration kubectl get cm nginx-configuration -n ingress-nginx -o yaml添加如上配置 compute-full-forwarded-for: “true” forwarded-for-header: X-Forwa…...

6种最常用的3D点云语义分割AI模型对比
由于增强现实/虚拟现实的发展及其在计算机视觉、自动驾驶和机器人领域的广泛应用,点云学习最近引起了人们的关注。 深度学习已成功用于解决 2D 视觉问题,然而,由于其处理面临独特的挑战,深度学习技术在点云上的使用仍处于起步阶段…...
UG NX二次开发(C#)-获取UI中选择对象的handle值
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 1、前言2、设计一个简单的UI界面3、创建工程项目4、测试结果1、前言 我在哔哩哔哩的视频中看到有人问我如何获取UI选择对象的Handle,本来想把Tag、Taggedobject、Handle三者的关系讲一下,然后看…...

win10,WSL的Ubuntu配python3.7手记
1.装linux 先在windows上安装WSL版本的Ubuntu Windows10系统安装Ubuntu子系统_哔哩哔哩_bilibili (WSL2什么的一直没搞清楚) 图形界面会出一些问题,注意勾选ccsm出的界面设置 win10安装Ubuntu16.04子系统,并开启桌面环境_win…...

02-Zookeeper实战
上一篇:01-Zookeeper特性与节点数据类型详解 1. zookeeper安装 Step1: 配置JAVA环境,检验环境: java -versionStep2: 下载解压 zookeeper wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeepe…...

【C语言深入理解指针(1)】
1.内存和地址 1.1内存 在讲内存和地址之前,我们想有个⽣活中的案例: 假设有⼀栋宿舍楼,把你放在楼⾥,楼上有100个房间,但是房间没有编号,你的⼀个朋友来找你玩,如果想找到你,就得挨…...

模拟实现简单的通讯录
前言:生活中处处都会看到或是用到通讯录,今天我们就通过C语言来简单的模拟实现一下通讯录。 鸡汤:跨越山海,终见曙光! 链接:gitee仓库:代码链接 目录 主函数声明部分初始化通讯录实现扩容的函数增加通讯录所…...

rabbitMQ死信队列快速编写记录
文章目录 1.介绍1.1 什么是死信队列1.2 死信队列有什么用 2. 如何编码2.1 架构分析2.2 maven坐标2.3 工具类编写2.4 consumer1编写2.5 consumer2编写2.6 producer编写 3.整合springboot3.1 架构图3.2 maven坐标3.3 构建配置类,创建exchange,queue&#x…...
数位dp,338. 计数问题
338. 计数问题 - AcWing题库 给定两个整数 a 和 b,求 a 和 b 之间的所有数字中 0∼90∼9 的出现次数。 例如,a1024,b1032,则 a 和 b 之间共有 9 个数如下: 1024 1025 1026 1027 1028 1029 1030 1031 1032 其中 0 出…...
如何解决git clone http/https仓库失败(403错误)
本来不打算写这篇文章,但是后来又遇到这个问题忘了之前是怎么解决的了。 一般情况下,个人使用 GitHub 等平台时是使用 SSH 协议的,这样不光方便管理可访问用户,也保证了安全性。但是 GitHub 上仓库的 SSH 地址是要登陆才能看到&a…...

华为云云耀云服务器L实例评测 | 实例评测使用之硬件性能评测:华为云云耀云服务器下的硬件运行评测
华为云云耀云服务器L实例评测 | 实例评测使用之硬件性能评测:华为云云耀云服务器下的硬件运行评测 介绍华为云云耀云服务器 华为云云耀云服务器 (目前已经全新升级为 华为云云耀云服务器L实例) 华为云云耀云服务器是什么华为云云耀…...

Elasticsearch:使用 Elasticsearch 进行语义搜索
在数字时代,搜索引擎在通过浏览互联网上的大量可用信息来检索数据方面发挥着重要作用。 此方法涉及用户在搜索栏中输入特定术语或短语,期望搜索引擎返回与这些确切关键字匹配的结果。 虽然关键字搜索对于简化信息检索非常有价值,但它也有其局…...
JVM的主要组成及其作用
jvm主要组成部分有: 类加载器、运行时数据区 (内存结构)、执行引擎、本地接口库、垃圾回收机制 Java程序运行的时候,首先会通过类加载器把Java 代码转换成字节码。然后运行时数据区再将字节码加载到内存中,但字节码文件只是JVM 的一套指令集规范…...

会议AISTATS(Artificial Intelligence and Statistics) Latex模板参考文献引用问题
前言 在看AISTATS2024模板的时候,发现模板里面根本没有教怎么引用,要被气死了。 如下,引用(Cheesman, 1985)的时候,模板是自己手打上去的?而且模板提供的那三个引用,根本也没有Cheesman这个人,…...

2023最新外贸建站:WordPress搭建外贸独立站零基础小白保姆级教程
想从零开始建立一个外贸自建站,那么你来对地方了。 如果你还在找外贸建站或者是WordPress建站教程,不妨看看这篇文章,本教程涵盖了2023最新的外贸建站教程,你将学会使用WordPress自建外贸独立站,步骤包括购买域名主机…...

UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...

利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...