CAS是英文单词Compare and Swap的缩写,翻译过来就是比较并替换。

CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。

更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
我们看一个例子:

  1. 在内存地址V当中,存储着值为10的变量。


2. 此时线程1想把变量的值增加1.对线程1来说,旧的预期值A=10,要修改的新值B=11.

3. 在线程1要提交更新之前,另一个线程2抢先一步,把内存地址V中的变量值率先更新成了11。

4. 线程1开始提交更新,首先进行A和地址V的实际值比较,发现A不等于V的实际值,提交失败。

5. 线程1 重新获取内存地址V的当前值,并重新计算想要修改的值。此时对线程1来说,A=11,B=12。这个重新尝试的过程被称为自旋。

6. 这一次比较幸运,没有其他线程改变地址V的值。线程1进行比较,发现A和地址V的实际值是相等的。

7. 线程1进行交换,把地址V的值替换为B,也就是12.

从思想上来说,synchronized属于悲观锁,悲观的认为程序中的并发情况严重,所以严防死守,CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去重试更新。

在java中除了上面提到的Atomic系列类,以及Lock系列类夺得底层实现,甚至在JAVA1.6以上版本,synchronized转变为重量级锁之前,也会采用CAS机制。

CAS的缺点:

1) CPU开销过大

在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,会给CPU带来很到的压力。

2) 不能保证代码块的原子性

CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。

3) ABA问题

这是CAS机制最大的问题所在。

我们现在来说什么是ABA问题。假设内存中有一个值为A的变量,存储在地址V中。

此时有三个线程想使用CAS的方式更新这个变量的值,每个线程的执行时间有略微偏差。线程1和线程2已经获取当前值,线程3还未获取当前值。

接下来,线程1先一步执行成功,把当前值成功从A更新为B;同时线程2因为某种原因被阻塞住,没有做更新操作;线程3在线程1更新之后,获取了当前值B。

在之后,线程2仍然处于阻塞状态,线程3继续执行,成功把当前值从B更新成了A。

最后,线程2终于恢复了运行状态,由于阻塞之前已经获得了“当前值A”,并且经过compare检测,内存地址V中的实际值也是A,所以成功把变量值A更新成了B。

看起来这个例子没啥问题,但如果结合实际,就可以发现它的问题所在。

我们假设一个提款机的例子。假设有一个遵循CAS原理的提款机,小灰有100元存款,要用这个提款机来提款50元。

由于提款机硬件出了点问题,小灰的提款操作被同时提交了两次,开启了两个线程,两个线程都是获取当前值100元,要更新成50元。

理想情况下,应该一个线程更新成功,一个线程更新失败,小灰的存款值被扣一次。

线程1首先执行成功,把余额从100改成50.线程2因为某种原因阻塞。这时,小灰的妈妈刚好给小灰汇款50元。

线程2仍然是阻塞状态,线程3执行成功,把余额从50改成了100。

线程2恢复运行,由于阻塞之前获得了“当前值”100,并且经过compare检测,此时存款实际值也是100,所以会成功把变量值100更新成50。

原本线程2应当提交失败,小灰的正确余额应该保持100元,结果由于ABA问题提交成功了。

怎么解决呢?加个版本号就可以了。

真正要做到严谨的CAS机制,我们在compare阶段不仅要比较期望值A和地址V中的实际值,还要比较变量的版本号是否一致。

我们仍然以刚才的例子来说明,假设地址V中存储着变量值A,当前版本号是01。线程1获取了当前值A和版本号01,想要更新为B,但是被阻塞了。

这时候,内存地址V中变量发生了多次改变,版本号提升为03,但是变量值仍然是A。

随后线程1恢复运行,进行compare操作。经过比较,线程1所获得的值和地址的实际值都是A,但是版本号不相等,所以这一次更新失败。

什么事ABA问题?怎么解决?
当一个值从A变成B,又更新回A,普通CAS机制会误判通过检测。
利用版本号比较可以有效解决ABA问题。

CAS详解,如何理解CAS!相关推荐

  1. Java中CAS详解

    转载自  Java中CAS详解 在JDK 5之前Java语言是靠synchronized关键字保证同步的,这会导致有锁 锁机制存在以下问题: (1)在多线程竞争下,加锁.释放锁会导致比较多的上下文切换 ...

  2. BlockChain:BlockChain周边概念详解+个人理解

    BlockChain:BlockChain周边概念详解+个人理解 目录 1.拜占庭将军问题 2.共识机制 1.区块链的共识机制目前有以下几种 PoW PoS DPoS 其他共识机制 1.拜占庭将军问题 ...

  3. 对tcp三次握手的详解之 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)

    重要 !!!!!!!!!       转载自[怀揣梦想,努力前行] 对tcp三次握手的详解之 理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number) ...

  4. tf.unstack() 详解 —》理解为主

    tensorflow中的tf.unstack(): 解释:这是一个对矩阵进行分解的函数,以下为关键参数解释: value:代表需要分解的矩阵变量(其实就是一个多维数组,一般为二维): axis:指明对 ...

  5. Java CAS详解

    CAS,即Compare and Swap,是基于硬件级别的指令实现的同步原语,Java并发包java.utile.concurrent许多同步类基于CAS构建,因此可见CAS的重要性: 定义 处理器 ...

  6. 谷粒商城RabbitMQ锁库存逻辑详解--新理解(长文警告)

    前言 不废话,上来就说,代码我会放挺多,写过这个项目的自然能懂,如果真的像理解的请认真看哦 分析 /*出现的问题:扣减库存成功了,但是由于网络原因超时,出现异常,导致订单事务回滚,库存事务不回滚(解决 ...

  7. tf.cancat() 详解 —》理解为主

    tensorflow 中用来拼接张量的函数tf.concat(),用法: tf.concat([tensor1, tensor2,...], axis) 代码详解-维度变化理解: # 参考:https ...

  8. cas内外网同时访问_并发编程高手必知——CAS详解

    一.java内存模型:JMM 在内存模型当中定义一个主内存,所有声明的实例变量都存在于主内存当中,主内存的数据会共享给所有线程,每一个线程有一个块工作内存,工作内存当中主内存数据的副本当更新数据时,会 ...

  9. CAS详解及ABA问题的解决

    序言 由于最近项目上遇到了高并发问题,而自己对高并发,多线程这里的知识点相对薄弱,尤其是基础,所以想系统的学习一下,以后可能会出一系列的JUC文章及总结 ,同时也为企业级的高并发项目做好准备. 本文是 ...

最新文章

  1. 用 Pyecharts 制作炫酷的可视化大屏
  2. 关于软件开发的随想,纯属意淫
  3. Delphi 复习代码
  4. linux输出重定向%3e退出,Linux学习笔记——第二章:Linux的用户接口与文本编辑器...
  5. 怎么理解python的__init___理解Python中super()和__init__()方法
  6. 添加nginx为系统服务(service nginx start/stop/restart)
  7. 黑马Python + 人工智能学习笔记
  8. 如何使用Angular的@Input()装饰器
  9. 30分钟了解C 11新特性
  10. 在android中ScrollView嵌套ScrollView解决方案
  11. 吴恩达深度学习编程作业汇总
  12. STM32之DAC原理
  13. flex4 BlazeDS 入门及配置
  14. 异构系统间Web Service通讯框架小结(补完企划)
  15. \r \n \r\n的区别
  16. mysql 关系图 工具_ER图绘制软件-DbSchema数据库关系图设计器下载v8.1.7-领航下载站...
  17. IntelliJ IDEA使用教程(动图详解):Mac 系统下安装 IntelliJ IDEA
  18. go mod 使用方法
  19. 人工智能建立本体库_吕律:人工智能和本体论
  20. Flink 命令行提交参数

热门文章

  1. Android混淆总结篇(一)
  2. android开源框架总结
  3. #医疗算法招聘:【医学影像AI公司-图像算法工程师】(招2人)
  4. 数据分析中的常用数学模型实战教程笔记(下)
  5. 802.1X与portal的无线认证
  6. java文本压缩算法_java 什么算法压缩文件最小
  7. 如何在Word中优雅地插入代码块,论文、竞赛专用
  8. github标准pull request提交流程
  9. js对象数组去除重复
  10. React报错Warning: This synthetic event is reused for performance reasons. If you‘re seeing this, 解决方法