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,一篇就够了相关推荐

  1. 智能车浅谈——过程通道篇

    文章目录 前言 过程通道 模拟量输入通道 模拟量输出通道 开关量输入/输出通道 小结 模拟信号的调理 信号放大电路 滤波限幅电路 开关量信号调理 信号转换电路 滤波电路 保护电路 触点消抖 光耦隔离 ...

  2. 智能车浅谈——方向控制篇

    文章目录 前言 自动控制理论 人工控制系统 自动控制 方向控制 典型环节对应 典型环节分析 给定环节与给定量 比较环节与偏差量 控制环节与控制量 执行机构 舵机 PWM技术 舵机中值及限幅 转向控制 ...

  3. 智能车浅谈——控制规律篇

    文章目录 前言 计算机控制系统 常用控制规律 PID控制 比例(P)控制器 比例积分(PI)控制器 比例积分微分(PID)控制 位置式PID 增量式PID 数字PID控制算法的改进 PID参数整定 小 ...

  4. 智能车浅谈——电机控制篇

    文章目录 前言 运动控制系统 被控对象 执行机构 控制器 反馈环节 M法测速: T法测速 小结 直流调速系统 桥式可逆PWM变换器 (1)正向运行 (2)反向运行 总结 智能车系列文章汇总 前言 之前 ...

  5. 浅谈Java锁,与JUC的常用类,集合安全类,常用辅助类,读写锁,阻塞队列,线程池,ForkJoin,volatile,单例模式不安全,CAS,各种锁

    浅谈JUC的常用类 JUC就是java.util.concurrent-包下的类 回顾多线程 Java默认有几个线程? 2 个 mian.GC Java 真的可以开启线程吗? 开不了,点击源码得知:本 ...

  6. 智能车浅谈——手把手让车跑起来(电磁篇)

    文章目录 前言 材料准备 备赛组 车模 硬件 练习组 车模 硬件方案 整车原理 赛道信息获取及转向原理 工字电感 运放模块 转向原理 元素判断 电机及舵机控制原理 代码实现 效果欣赏 总结 17届完赛 ...

  7. 智能车浅谈——硬件篇

    目录 初识小车 硬件系统 1.电源系统 线性电源 开关电源 2.人机交互系统 3.MCU最小系统 4.传感器系统 摄像头 电感 编码器 5.驱动系统 机械结构 17届完赛代码 智能车系列文章汇总 前言 ...

  8. 智能车浅谈——抗干扰技术硬件篇

    文章目录 前言 干扰 什么是干扰 干扰窜入的主要途径 干扰的分类 硬件抗干扰技术 控制系统的电源保护技术 输入/输出传输线的抗干扰措施 I/O接口的抗干扰措施 接地技术 总结 智能车系列文章汇总 前言 ...

  9. 智能车浅谈——抗干扰技术软件篇

    文章目录 软件抗干扰技术 数字信号的抗干扰措施 数字输入信号软件抗干扰措施 数字输出信号软件抗干扰措施 数字滤波 算术平均值滤波 中值滤波 滑动平均滤波 归一化 差比和 CPU及程序的抗干扰措施 复位 ...

最新文章

  1. 使用subprocessm模块管理进程
  2. python第四十一天---作业:简单FTP
  3. Android MVVM封装,MVVMFramework
  4. I春秋——web Write up(一)
  5. 思科设备debug命令的使用
  6. VS2013、VS2015中,新建项目没有看到解决方案的问题(已解决)
  7. uni-app中v-html中的元素添加样式
  8. CSS最基础的语法和三种引入方式
  9. iOS 几种打包方式
  10. 28.earch in Rotated Sorted Array(排序旋转数组中查找)
  11. IDEA 出现编译错误 Multi-catches are not supported a this language level 解决方法
  12. Unirech阿里云国际版云服务器代充-使用Python批量创建实例
  13. 2022年长沙市成人高考疫情防控政策
  14. linux下raid(md)驱动源码解析
  15. 某基于DEDECMS5.5网站的安全检测初步报告
  16. POJ 2152 fire / SCU 2977 fire(树型动态规划)
  17. Sendmail大全
  18. 【如何成为一名优秀的项目经理】跟着本文8个步骤走下去
  19. PTA-面向对象程序设计实验8:综合-模拟Qt框架
  20. 整蛊朋友的 Python 程序

热门文章

  1. stata生成脉冲响应图怎么导出_Stata:面板VAR模型(pvar2命令)
  2. Java创建一个简单的图书管理系统
  3. 微信小程序:全新动态视频壁纸下载支持多种分类短视频另外也有静态壁纸
  4. Java入门-Java学习路线课程面试篇:取商 / 和取余(模) % 符号的使用
  5. 为什么新品发布上架之后会没有流量,新品应该怎么发布?
  6. php-opencv身份证识别,python opencv实现证件照换底功能
  7. Laravel—Purifier扩展包防止XSS攻击
  8. 计算机增加一个硬盘怎么设置方法,电脑加硬盘【操作教程】
  9. 如何在C ++ 中分割PDF档案?试试Aspose
  10. 【五一创作】Qt quick基础1(包含基本元素Text Image Rectangle的使用)