浅谈CAS,一篇就够了
wshanshi:喵桑说,我总结完CAS就带我去吃羊蝎子火锅…干饭那必须整起啊…
一、什么是CAS?
CAS:Compare and Swap。从字面意义上来说,就是先进行比较,然后替换。
它是乐观锁思想的一种实现,尤其是在并发量大的业务场景下保证单个实例的原子性,使用较为频繁。java类库中java.util.concurrent.atomic包下一些方法,也均使用CAS处理。
二、悲观锁与乐观锁
CAS是乐观锁思想的一种体现,那乐观锁和悲观锁有什么区别呢?
2.1、悲观锁
悲观锁常见使用是synchronized修饰的代码块或者方法。
在操作数据之前加锁,直到数据操作完成,锁被释放之后,其它线程才可以操作该数据。比如,mysql数据库锁就是悲观锁。
2.2、乐观锁
数据操作不加锁,每次提交之前获取最新值与原获取值进行对比,数据未变更时操作,否则自旋。
2.3、区别
- 乐观锁是并行的,悲观锁是串行的。
乐观锁:
悲观锁:
- 乐观锁实质并未“加锁”,悲观锁是加了锁的(synchronized)。
三、CAS原理
Compare and Swap,比较并替换。说白了就是:在操作提交之前,与原获取到的值先进行比较,判断这个值有没有被修改。如果未被修改,操作修改。如果已被修改,则重新获取值,提交之前再比较…
举个栗子:关于《损友经常在群里偷偷改我的头衔》这件事。
线程a获取到我的信息,并操作将我的头衔由“三婶”改为了“王胖虎”。
之后线程b获取到我的头衔为“王胖虎”,又修改了“王胖虎”为“三婶”。
这是正常的一种非并发流程的体现,我们再来看下面一种情况:
假设线程a和线程b均获取到我的名字“三婶”。且线程a操作修改了“三婶”为“王胖虎”。
若a线程成功操作之后,线程b在修改提交前获取名称,发现实际读到的头衔是“王胖虎”。
对比预期值(三婶),发现"三婶"!=“王胖虎”(被修改了)。这时按照CAS原理,就会再次获取预期值(此时预期值为:王胖虎),且提交前获取内存值,进行对比…判断是否一致…
CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,需要替换的新值B。
计算规则是:当需要更新一个变量的值的时候,仅当变量的预期值A(原获取)和内存地址V(提交前获取)中实际值相同的时候,才会把内存地址V对应的值替换成B。
如下图示例:
四、核心Unsafe类库
该类可直接操作内存,所以效率高。且Unsafe类和常量均使用final修饰,单例模式实现,不可继承。通过下图所示静态方法getUnsafe进行实例化,实例化在static块中操作的。
Unsafe可以设置读写某个属性,如下图所示。
volatile 保证了不同线程对共享变量操作的可见性,也就是说一个线程修改了 volatile 修饰的变量,当修改后的变量写回主内存时,其他线程能立即看到最新的值。
五、CAS优缺点
优点:在并发问题不严重的时候,性能方面比synchronized要快。
缺点:不能确保代码块的原子性。因为CAS机制确保的是一个变量的原子性操作,并不能保证整个代码块的原子性。如果多个变量共同进行原子性的更新操作,就需要用lock或者synchronized了。
5.1、自旋
假设线程a和线程b均获取到我的名字“三婶”。线程a操作修改了“三婶”为“王胖虎”,之后线程b在提交前获取名称,发现读到的是“王胖虎”。对比不一致,就会再次获取。
假如这个时候有个线程c把“王胖虎”改为了“胖虎”,线程b读取时又不一致了。这个时候就会一直获取,一直对比…
这种现象称为“自旋”。
5.2、ABA情况
还拿上述的栗子来说:
假设线程a和线程b均获取到我的名字“三婶”。然后线程a操作修改了“三婶”为“王胖虎”。
如果这个时候有个线程c成功操作:将“王胖虎”改为了“三婶”。线程b在提交前获取名称,发现读到的是“三婶”,它会以为“这个值并没有发生变化”。
但实质上,这个值可能是多次被修改后,恰巧变为了原始值的一种情况,也就是所谓的ABA.
六、如何避免ABA情况?
6.1、加版本号
每次操作compareAndSwap后给数据的版本号加1,再次compareAndSwap的时候不仅比较数据,也比较版本号,值相同,若是版本号不同,就不执行成功。
java.util.concurrent.atomic包中提供了AtomicStampedReference来解决该问题。
AtomicStampedReference 内部维护了一个 Pair的数据结构:reference(数据体)、stamp(版本)两个部分。该数据结构用volatile修饰,保证了线程可见性。
核心方法为:compareAndSet方法。该方法中,expectedReference:表示预期值,newReference:表示新的值,expectedStamp:表示预期版本号,newStamp表示新的版本号。
从数据和版本号两个方面来判断传入的参数是否符合 Pair 的预期,有一个不符合就返回false。
可以看到,这里底层也是使用了cas。预期值为“三婶”,版本号为0。新值为“王胖虎”,新版本号为1。
而casPair实质上调用的是UNSAFE.compareAndSwapObject()方法。
由此可见,AtomicStampedReference是通过加版本号来解决ABA问题的。对于加版本号,compareAndSwapObject只能对比交互一个对象,所以将数据和版本号放到一个对象里就可以解决问题了。
浅谈CAS,一篇就够了相关推荐
- 智能车浅谈——过程通道篇
文章目录 前言 过程通道 模拟量输入通道 模拟量输出通道 开关量输入/输出通道 小结 模拟信号的调理 信号放大电路 滤波限幅电路 开关量信号调理 信号转换电路 滤波电路 保护电路 触点消抖 光耦隔离 ...
- 智能车浅谈——方向控制篇
文章目录 前言 自动控制理论 人工控制系统 自动控制 方向控制 典型环节对应 典型环节分析 给定环节与给定量 比较环节与偏差量 控制环节与控制量 执行机构 舵机 PWM技术 舵机中值及限幅 转向控制 ...
- 智能车浅谈——控制规律篇
文章目录 前言 计算机控制系统 常用控制规律 PID控制 比例(P)控制器 比例积分(PI)控制器 比例积分微分(PID)控制 位置式PID 增量式PID 数字PID控制算法的改进 PID参数整定 小 ...
- 智能车浅谈——电机控制篇
文章目录 前言 运动控制系统 被控对象 执行机构 控制器 反馈环节 M法测速: T法测速 小结 直流调速系统 桥式可逆PWM变换器 (1)正向运行 (2)反向运行 总结 智能车系列文章汇总 前言 之前 ...
- 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁
浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...
- 智能车浅谈——手把手让车跑起来(电磁篇)
文章目录 前言 材料准备 备赛组 车模 硬件 练习组 车模 硬件方案 整车原理 赛道信息获取及转向原理 工字电感 运放模块 转向原理 元素判断 电机及舵机控制原理 代码实现 效果欣赏 总结 17届完赛 ...
- 智能车浅谈——硬件篇
目录 初识小车 硬件系统 1.电源系统 线性电源 开关电源 2.人机交互系统 3.MCU最小系统 4.传感器系统 摄像头 电感 编码器 5.驱动系统 机械结构 17届完赛代码 智能车系列文章汇总 前言 ...
- 智能车浅谈——抗干扰技术硬件篇
文章目录 前言 干扰 什么是干扰 干扰窜入的主要途径 干扰的分类 硬件抗干扰技术 控制系统的电源保护技术 输入/输出传输线的抗干扰措施 I/O接口的抗干扰措施 接地技术 总结 智能车系列文章汇总 前言 ...
- 智能车浅谈——抗干扰技术软件篇
文章目录 软件抗干扰技术 数字信号的抗干扰措施 数字输入信号软件抗干扰措施 数字输出信号软件抗干扰措施 数字滤波 算术平均值滤波 中值滤波 滑动平均滤波 归一化 差比和 CPU及程序的抗干扰措施 复位 ...
最新文章
- 使用subprocessm模块管理进程
- python第四十一天---作业:简单FTP
- Android MVVM封装,MVVMFramework
- I春秋——web Write up(一)
- 思科设备debug命令的使用
- VS2013、VS2015中,新建项目没有看到解决方案的问题(已解决)
- uni-app中v-html中的元素添加样式
- CSS最基础的语法和三种引入方式
- iOS 几种打包方式
- 28.earch in Rotated Sorted Array(排序旋转数组中查找)
- IDEA 出现编译错误 Multi-catches are not supported a this language level 解决方法
- Unirech阿里云国际版云服务器代充-使用Python批量创建实例
- 2022年长沙市成人高考疫情防控政策
- linux下raid(md)驱动源码解析
- 某基于DEDECMS5.5网站的安全检测初步报告
- POJ 2152 fire / SCU 2977 fire(树型动态规划)
- Sendmail大全
- 【如何成为一名优秀的项目经理】跟着本文8个步骤走下去
- PTA-面向对象程序设计实验8:综合-模拟Qt框架
- 整蛊朋友的 Python 程序
热门文章
- stata生成脉冲响应图怎么导出_Stata:面板VAR模型(pvar2命令)
- Java创建一个简单的图书管理系统
- 微信小程序:全新动态视频壁纸下载支持多种分类短视频另外也有静态壁纸
- Java入门-Java学习路线课程面试篇:取商 / 和取余(模) % 符号的使用
- 为什么新品发布上架之后会没有流量,新品应该怎么发布?
- php-opencv身份证识别,python opencv实现证件照换底功能
- Laravel—Purifier扩展包防止XSS攻击
- 计算机增加一个硬盘怎么设置方法,电脑加硬盘【操作教程】
- 如何在C ++ 中分割PDF档案?试试Aspose
- 【五一创作】Qt quick基础1(包含基本元素Text Image Rectangle的使用)