【Java 并发编程】CAS 原理解析

news2024/11/24 4:55:55

CAS 原理解析

  • 1. 什么是 CAS?
    • 1.1 悲观锁与乐观锁
    • 1.2 CAS 是什么?
  • 2. CAS 核心源码
  • 3. CAS 实现原子操作的三大问题
    • 3.1 ABA 问题
    • 3.2 循环性能开销
    • 3.3 只能保证一个变量的原子操作
  • 4. synchronized、volatile、CAS 比较

1. 什么是 CAS?

1.1 悲观锁与乐观锁

悲观锁的原理是每次实现数据库的增删改的时候都进⾏阻塞,防⽌数据发⽣脏读。

乐观锁的原理是在数据库更新的时候,⽤⼀个 version 字段来记录版本号,然后通过⽐较是不是⾃⼰要修改的版本号再进⾏修改。这其中就引出了⼀种⽐较交换的思路来实现数据的⼀致性,事实上,CAS 也是基于这样的原理。

1.2 CAS 是什么?

CAS 是指 Compare And Swap比较并交换,是一种无锁原子算法,在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。

JAVA 底层是 C++ 实现,映射到操作系统就是一条 cmpxchg 硬件汇编指令(保证原子性),其作用就是让 CPU 将内存值更新为新值,但是有个条件,内存值必须与期望值相同。并且 CAS 操作无需用户态与内核态切换,直接在用户态对内存进行读写操作(意味着不会阻塞/线程上下文切换)。

java.util.concurrent.atomic 包中的原子类就是通过 CAS 来实现了乐观锁。

CAS 算法涉及到三个操作数:

  • 需要更新的内存变量值 V(volatile)

  • 上一次从内存中读取,进行比较的预期原值 E(except)

  • 要写入的新值 N(new)

当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值(“比较 + 更新” 整体是一个原子操作),否则不会执行任何操作,这就是一次 CAS 的操作。一般情况下,“更新” 是一个不断重试的操作。

在这里插入图片描述
简单说,CAS 需要你额外给出一个期望值,也就是你认为这个变量现在应该是什么样子的,如果变量不是你想象的那样,说明它已经被别人修改过了,你只需要重新读取,设置新期望值,再次尝试修改就好了。

2. CAS 核心源码

java.util.concurrent.atomic 包中的原子类就是通过 CAS 思想来实现,CAS 思想实现靠的是 Unsafe 类。以 AtomicInteger 为例:

在这里插入图片描述

Unsafe 类是 CAS 的核心类,由于 Java 方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe 相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像C的指针一样直接操作内存。

valueOffset 表示当前类对象中使用变量的偏移量,Unsafe 就是根据内存偏移地址获取数据的。

value 要修改的值,用 volatile 修饰,保证了多线程之间的内存可见性。

一起看一下 AtomicInteger.getAndIncrement() 方法是怎么替换内容的:

public final int getAndIncrement() {
	// 拿到当前对象和当前对象的地址,然后加一。
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

// var1 就是当前对象,var2 就是当前对象的内存地址,var4 就是要加的数
public final int getAndAddInt(Object var1, long var2, int var4) {
	// 旧的预期值
    int var5;
    do {
        // 获得当前 var2 地址中的值,可以理解为从当前主物理内存中拷贝一份到自己的本地内存
        var5 = this.getIntVolatile(var1, var2);
    } 
	// 如果var1 = var5,执行var5 + var4,执行成功返回 true,跳出 while 循环,执行失败返回false,自旋
	while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *)index_oop_from_field_offset_long(p, offset);

  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
} UNSAFE_END

首先获取 var5 旧的预期值,然后调用底层代码 Unsafe_CompareAndSetInt。下面看 Unsafe_CompareAndSetInt 怎么做的:

第一步通过 JNIHandles::resolve() 获取 obj 在内存中 OOP 实例;
第二步根据成员变量 value 反射后计算出的内存偏移值 offset 去内存中取指针 addr;
最后通过 Atomic::cmpxchg(x, addr, e) 实现 CAS。

3. CAS 实现原子操作的三大问题

3.1 ABA 问题

并发环境下,假设初始条件是 A,去修改数据时,发现是 A 就会执行修改。但是看到的虽然是 A,中间可能发生了 A 变 B,B 又变回 A 的情况。此 A 已经非彼 A,数据即使成功修改,也可能有问题。

怎么解决 ABA 问题?

加版本号。

每次修改变量,都在这个变量的版本号上加1,这样发生 A->B->A 时,虽然 A 的值没变,但是它的版本号已经变了,再判断版本号就会发现此时的 A 已经被改过了。参考乐观锁的版本号,这种做法可以给数据带上了一种时效性的检验。

Java 提供了 AtomicStampReference 类,它的 compareAndSet 方法首先检查当前的对象引用值是否等于预期引用,并且当前版本号(Stamp)标志是否等于预期标志,如果全部相等,则以原子方式将引用值和版本号标志的值更新为给定的更新值。

3.2 循环性能开销

自旋 CAS,如果一直循环执行,一直不成功,会给 CPU 带来非常大的执行开销。

怎么解决循环性能开销问题?

在 Java 中,很多使用自旋 CAS 的地方,会有一个自旋次数的限制,超过一定次数,就停止自旋。

3.3 只能保证一个变量的原子操作

CAS 保证的是对一个变量执行操作的原子性,如果对多个变量操作时,CAS 目前无法直接保证操作的原子性的。

怎么解决只能保证一个变量的原子操作问题?

  • 可以考虑改用锁来保证操作的原子性
  • 可以考虑合并多个变量,将多个变量封装成一个对象,从 Java 1.5 开始,JDK 提供了 AtomicReference 类来保证引用对象之间的原子性,就可以把多个变量放在一个对象里来进行 CAS 操作。

4. synchronized、volatile、CAS 比较

(1)synchronized 是悲观锁,属于抢占式,会引起其他线程阻塞。

(2)volatile 提供多线程共享变量可见性和禁止指令重排序优化。

(3)CAS 是基于冲突检测的乐观锁(非阻塞)。

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

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

相关文章

物业企业多种类型合同,用泛微今承达实现统一数字化管理

随着物业业务的不断发展,物业服务越来越精细化、专业化,旨在为居民社区提供更便利的服务。 物业企业提供多种形态、全方位、立体式的综合服务,包括基础物业服务、业主增值服务(空间运营收入、房屋经纪、电商服务、社区金融、家政服务及养老服…

手撕-扫雷

一、前言-认识扫雷 二、打印菜单 三、创建棋盘并初始化 四、打印棋盘 五、布置雷 六、排查雷(统计坐标周围雷的个数) 七、扫雷代码全析(game.h game.c test.c) 铁汁们,今天给大家分享一篇扫雷游戏的实现&#…

Python快速批量修改图片尺寸

之前我们写过快速批量获取图片的大小,该文章链接在这里:Python每日一个知识点9----批量输出图片尺寸 今天我们分享一个快速批量修改图片尺寸的小脚本,我们一下看一下 先看一下目录结构: 文件夹:【原始图片】&#xf…

在阿里做了6年软件测试,4月无情被辞,想给划水的兄弟提个醒

先简单交代一下背景吧,某不知名 985 的本硕,17 年毕业加入阿里,以“人员优化”的名义无情被裁员,之后跳槽到了有赞,一直从事软件测试的工作。之前没有实习经历,算是6年的工作经验吧。 这6年之间完成了一次…

RabbitMQ 运维备忘录(一)

文章目录 1. 基础信息参考一些官方的链接单节点安装 RabbitMQ开启 web 端管理界面 2. 一些基本操作应用管理服务端口开放信息用户管理虚拟主机 vhost 管理web 界面管理队列信息查询交换器信息查询绑定关系的信息查询TCP/IP 连接信息查询信道信息查询消费者信息查询服务状态查询…

英伟达开放BEVFusion部署源代码,边缘端实时运行(高达25FPS)

以下文章来源于微信公众号:集智书童 链接:https://mp.weixin.qq.com/s/6BWohe2FxRN8E-yyp_32fg 本文仅用于学术分享,如有侵权,请联系后台作删文处理 引言: 众所周知,雷达与相机的融合方案由于稀疏卷积的原…

STM32F4_PWM DAC

目录 1. 为什么需要使用PWM DAC 2. PWM DAC简介 3. 硬件设计 4. 软件设计 4.1 main.c 4.2 PWMDAC.c 4.3 PWMDAC.h 1. 为什么需要使用PWM DAC 虽然STM32F4自带DAC模块,但是在有些时候,可能出现两个DAC不够用的情况(STM32F4只有两个DAC&…

看过来!一文揭秘为什么选TikTokTikTok数据怎么分析

为什么要选择TikTok开小店?以及商家在运营TikTok之前要了解掌握哪些TikTok相关数据?要分析哪些数据、怎么看TikTok数据才能让商家更快更及时地改进优化自己的TikTok营销策略?往下看。 一、选择TikTok开小店的原因: 1. TK作为一个…

《终身成长》笔记五——随心所欲地夸奖孩子们以成长为目标的努力过程

目录 经典摘录 校园暴力:对“复仇”的思考 施暴者:欺凌与评判息息相关 父母或老师:关于成功和失败的信息 是否意味着,当我们的孩子取得了不起的成就的时候,我们不能热情地表扬他们呢? 关于失败的信息 经…

在moveit2中实现四连杆及曲柄滑块

对于一些特殊的(比如说机械构件闭环的、存在被动关节的)运动模型,该如何用urdf模型描述该模型,且在MoveIt2中进行仿真?下面提供一个思路,仅供参考。 四连杆机构 结构介绍 该机构的组成如下图所示。 粉红…

加密解密软件VMProtect入门使用教程(九)许可制度之许可系统功能

VMProtect是新一代软件保护实用程序。VMProtect支持德尔菲、Borland C Builder、Visual C/C、Visual Basic(本机)、Virtual Pascal和XCode编译器。 同时,VMProtect有一个内置的反汇编程序,可以与Windows和Mac OS X可执行文件一起…

前端综合项目-个人博客网页设计

个人博客前端部分设计 文章目录 前端综合项目-个人博客网页设计1. 预计效果2. 公共样式设计2.1 背景设计2.2 导航栏设计2.3 博客列表页和博客详情页的共同内容2.3.1 页面划分css设计2.3.2 左侧card内容2.3.3 右侧article内容 3. 博客列表页4. 博客详情页5. 博客登录页5.1 页面划…

赛效:怎么在99医院库平台查询科室医生坐诊的时间?

如果想要了解更多生活中常用的工具的使用方法,可进入赛效官方网站查看应用软件或者应用问答栏目查看详情。 很多人在去医院就诊时,往往会提前查询下就诊医院当天坐诊的医生有哪些,如果可以直接在网上进行预约的话,通过网络预约要…

word中图标格式的美化

目录 1. 修改行号2. 调整图表格式2.1 方法1 (不推荐)2.2 方法2 3. 参考链接 1. 修改行号 2. 调整图表格式 要达到下图的效果 2.1 方法1 (不推荐) 利用“一系列格式操作”设置表格格式。(该方法不具有可复制性&#…

网络性能测试工具

什么是网络测试 网络测试是用于定量或定性测量 IT 基础架构性能的过程。这是一个原始级别的故障识别,不需要大量的历史数据。对于更高级的监视,使用网络监视工具。 什么是网络测试工具 网络测试工具是一组工具,可帮助测量网络各个方面的性…

ASO优化之如何选择手游的行业词和竞品词

应用商店的搜索关键词转化率占应用总下载转化率的65%。这表明,用户会知道自己想要什么,更倾向于去应用商店寻找特定的应用程序。因此如果能让他们相信我们的应用正是他们所寻找的,那么关键词优化就很重要了。 那么我们该如何选择手游的行业词…

中文文献如何查找下载最高效

提到查找下载中文文献我们就会想到知网、万方、维普等中国知名数据库,很多高校都订购了这些数据库资源,但各个高校订购的资源不仅内容上不一样,而且都不是数据库的全部资源,超出订购范围的文献资源是下载不到的,如下图…

探索iOS之CoreAudio核心音频框架

iOS的CoreAudio分为三层:应用服务层、驱动层、硬件层。其中,应用服务层包括:AudioQueue Service、AudioPlayer Service、AudioSession Service、AudioFile Service、AudioUnit等。 一、CoreAudio整体架构 CoreAudio的整体架构自顶向下是Ser…

求最小生成树(Prim算法与Kruskal算法与并查集)

目录 1、案例要求2、算法设计与实现2.1 Prim算法2.1.1 构造无向图2.1.2 编写Prim算法函数2.1.3 实现代码2.1.4 运行结果截图 2.2 Kruskal算法2.2.1 构造无向图2.2.2 编写并查集UnionFind类2.2.3 编写Kruskal算法2.2.4 实现代码2.2.5 运行结果截图 3、总结 1、案例要求 利用贪心…

CameraLink 高清医学影像分析模块

FMC-XM202是一款基于FMC接口标准的1路CameraLink Full模式(或者2路CameraLink Base模式)采集、1路HDMI(DVI)视频输出的子卡模块,该模块具有2个CameraLink端口(SDR,26PIN)&#xff0c…