文章目录

  • 阅读提示
  • CAS原理、ABA问题介绍
  • 真实业务场景
  • 如何解决ABA问题
  • CAS学习总结

阅读提示

本文将借助开保险柜的业务场景重点阐述误用AtomicBoolean引起的ABA问题,以及解决方案。基于此,请先深入理解CAS原理,以及其会产生的ABA问题。关于CAS原理和ABA问题的优秀博客已经存在很多,所以本文只简单介绍CAS原理,希望读者有此基础。

CAS原理、ABA问题介绍

CAS(Compare and Swap)是一种乐观锁机制。CAS有3个操作数,预期值A,内存值V,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

CAS算法实现一个重要前提需要线程A取出内存中某时刻的数据,而在下一时刻比较并替换,那么在这个时间差内的数据变化,线程A是无法感知。比如线程B在这个时间差内,就数据从A改成B,再从B改成A。线程A在下一时刻比较替换会成功。请你思考一下,这种ABA问题有什么危害?为什么要关心CAS原理带来的ABA问题?

比如,我们常用AtomicLong 表示车票数量 tickets,假设原始票数 tickets = 100,有一个线程A进行卖票,另一个线程B能增加票数,也能减少票数。当线程A进行卖票的时候,线程B增加10张票,又减少10张票,引起ABA问题,那么这个ABA问题对线程A有没有影响?如果有影响,会产生哪方面的线程安全问题(原子性、可见性、有序性)?如果没有影响,为什么没影响,不是说CAS会引起ABA问题吗,为什么这种场景不需要考虑ABA问题带来的影响。本文终极问题——到底什么情况下才要考虑解决CAS导致的ABA问题?

真实业务场景

// OPEN_OR_CLOSE 表示 保存高考卷的保险柜 开关,false表示close, true 表示 open,考虑下面的业务场景,
// 高考卷的保险柜,有且仅有两人有打开的权限。根据保密要求,人越少越好。当然,有且仅有一个人有权限,保密性更高,但是如果这人发生意外,就没人能打开保险柜
// 所以选两个人 既能照顾到保密性要求,又能减少突发事件的影响。
// 要求 2022-06-07 06:00:00 后,两个线程竞争去开保险柜,有且只有一人能打开,打开保险柜的人负责护送试题。(不考虑 synchronized 的实现方式)
// 假设这样的一种场景,张三、李四 竞争开柜的过程中,张三使手段让李四在开柜前,卡一下,确保自己能先开柜,然后拍照,获取试题,最后关上柜门
// 这个时候李四来开柜门,发现门的状态和教育部说的状态一样,都是close,然后李四拿走试题,张三过来说“李四啊,这次送试卷的任务就只能麻烦你了。”
// 然后 张三转手卖出试题,就算出了事情,教育厅也只能查李四。

这里先使用 AtomicBoolean 去实现,然后引出ABA问题。

public class AtomicBooleanExample {private static AtomicBoolean OPEN_OR_CLOSE = new AtomicBoolean(false);public static void main(String[] args) throws InterruptedException {boolean expect = OPEN_OR_CLOSE.get();boolean update = true;Thread zhangsan = new Thread(() -> {boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expect, update);System.out.println(Thread.currentThread().getName() + "开柜门:" + isOpen);// 省略 偷试题的操作boolean isClose = OPEN_OR_CLOSE.compareAndSet(OPEN_OR_CLOSE.get(), expect);System.out.println(Thread.currentThread().getName() + "关柜门:" + isClose);System.out.println(Thread.currentThread().getName() + "偷题是否成功" + (isOpen && isClose));}, "zhangsan");Thread lisi = new Thread(() -> {try {// 张三使手段,确保自己先执行完,真实场景可能用其它的手段zhangsan.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "打开之前门状态为:" + OPEN_OR_CLOSE.get());boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expect, update);System.out.println(Thread.currentThread().getName() + "打开之后门状态为:" + OPEN_OR_CLOSE.get() + ", " + Thread.currentThread().getName() + "开柜门是否成功:" + isOpen);}, "lisi");zhangsan.start();lisi.start();}
}
zhangsan开柜门:true
zhangsan关柜门:true
zhangsan偷题是否成功true
lisi打开之前门状态为:false
lisi打开之后门状态为:true, lisi开柜门是否成功:true

从结果可以看到,张三做了一次开关门操作(ABA操作)偷取了试题,然后李四顺利的打开保险柜,李四护送试题的同时,张三会卖出试题。显然,这时候使用AtomicBoolean 引起的ABA问题就需要我们去解决,因为我们关心保险柜门被打开的次数。保险柜门必须只能被打开一次。所以,我们需要记录保险柜门被操作的次数。Doug Lea 早就为JAVA 设计了AtomicStampedReference类以解决CAS的ABA问题。AtomicStampedReference的CAS操作是带有版本号的操作。

如何解决ABA问题

接下来用AtomicStampedReference 编写上面的程序。

public class AtomicStampedReferenceExample {private static AtomicStampedReference<Boolean> OPEN_OR_CLOSE = new AtomicStampedReference<>(false, 0);public static void main(String[] args) throws InterruptedException {boolean expectedReference = OPEN_OR_CLOSE.getReference();boolean newReference = true;int expectedStamp = OPEN_OR_CLOSE.getStamp();Thread zhangsan = new Thread(() -> {boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expectedReference, newReference, expectedStamp, expectedStamp + 1);System.out.println(Thread.currentThread().getName() + "开柜门:" + isOpen);// 省略 偷试题的操作boolean isClose = OPEN_OR_CLOSE.compareAndSet(newReference, expectedReference, OPEN_OR_CLOSE.getStamp(), OPEN_OR_CLOSE.getStamp() + 1);System.out.println(Thread.currentThread().getName() + "关柜门:" + isClose);System.out.println(Thread.currentThread().getName() + "偷题是否成功" + (isOpen && isClose));}, "zhangsan");Thread lisi = new Thread(() -> {try {// 张三使手段,确保自己先执行完,真实场景可能用其它的手段zhangsan.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "打开之前门状态为:" + OPEN_OR_CLOSE.getReference());boolean isOpen = OPEN_OR_CLOSE.compareAndSet(expectedReference, newReference, expectedStamp, expectedStamp + 1);System.out.println(Thread.currentThread().getName() + "打开之后门状态为:" + OPEN_OR_CLOSE.getReference() + ", "+ Thread.currentThread().getName() + "开柜门是否成功:" + isOpen);}, "lisi");// 当然这个地方最好用 发令枪做,同时起跑zhangsan.start();lisi.start();}}
zhangsan开柜门:true
zhangsan关柜门:true
zhangsan偷题是否成功true
lisi打开之前门状态为:false
lisi打开之后门状态为:false, lisi开柜门是否成功:false

控制台输出 “lisi打开之前门状态为:false”,但是李四开柜门没有成功!!!因为一开始的 expectedStamp 是0,被张三操作两次后变成2,李四去开柜门的时候,拿着0的版本号去开,compareAndSet 发现版本号不一致,这个开门的操作就会失败。

CAS学习总结

再次学习CAS原理的过程中,我看了一些视频和一些博客,都是说CAS原理是什么,会带来ABA问题,ABA问题如何解决。但是都没说清楚,到底什么情况下要解决CAS导致的ABA问题。就好比师傅教你一招降龙十八掌对付坏人(ABA问题的解决方案),却没告诉你什么样的坏人(什么情况需要考虑解决ABA问题),你使出降龙十八掌才有效。

如果业务只关心Atomic系列类的值,不关心值的变化次数(ABA会增加两次操作),那么CAS导致的ABA问题就无需考虑,例如卖票问题,你只关心总票数,不关心总票数波动的次数——别人退票后的票数增加或者其他人买票后票数减少。
反之,如果业务关心CAS的操作次数,例如本文的保险柜开关次数,就需要引入版本号解决ABA问题。

某个对象,在某个状态只能被操作一次,即针对数值变化次数有要求,不是针对数值。
不过,也有种可能是ABA会导致整体数据错误,比如经典的链表换链头的例子,也是ABA 问题,但是它是链的数据变化了,其实不是针对次数,而是针对链中数据。
学而不思则罔,思而不学则殆。

真实业务场景展现CAS原理的ABA问题及解决方案相关推荐

  1. 【云原生|实践指北】5:真实业务场景下云原生项目落地实践学习

    真实业务场景下云原生项目落地实践学习 写在前面的话 1.容器化的落地实践 搜题APP的云上之旅 2.Serverless的落地实践 某电商APP的Serverless改造之旅 3.云原生TKE的落地实 ...

  2. Java并发基石CAS原理以及ABA问题

    在学习CAS之前,先从一个简单的案例入手,进而引出CAS的基本使用: 1.基于CAS的网站计数器 需求: 我们开发一个网站,需要对访问量进行统计,用户每发送一次请求,访问量+1,如何实现? 我们模拟有 ...

  3. 用真实业务场景告诉你,高并发下如何设计数据库架构?

    目录: 用一个创业公司的发展作为背景引入 用多台服务器来分库支撑高并发读写 大量分表来保证海量数据下查询性能 读写分离来支撑按需扩容及性能提升 高并发下的数据库架构设计总结 这篇文章,我们来聊一下对于 ...

  4. 童玲:蚂蚁金服区块链在真实业务场景的实践与突破

    本文转载于公众号 区块链新金融 2017年2月19日星期日下午,虎嗅网联合蚂蚁金服在上海举办了区块链上道沙龙.沙龙邀请到了蚂蚁金服首席架构师童玲.众安科技CTO李雪峰.中国电子技术标准化研究院主任兼中 ...

  5. 千万数据量下的真实业务场景SQL性能优化!

    V-xin:ruyuanhadeng获得600+页原创精品文章汇总PDF 前 言 通过前几期文章的积累,现在我们的理论知识已经极为扎实了,这个时候就可以动手开始sql优化了,sql优化是非常重要,因为 ...

  6. 一文搞懂CAS,CAS原理分析及ABA问题详解

    什么是CAS CAS即Compare And Swap的缩写,翻译成中文就是比较并交换,其作用是让CPU比较内存中某个值是否和预期的值相同,如果相同则将这个值更新为新值,不相同则不做更新,也就是CAS ...

  7. NLP最新趋势,7个主流业务场景!

    1 深度之眼NLP项目实战安排 ⭐BAT级工程部署 项目意义:工程化部署是程序在开发完成之后,到线上正式运行整个过程中涉及到的多个环节的统称,主要包括:测试.GPU的分配和使用.微服务的封装.Dock ...

  8. 史上最复杂业务场景,逼出阿里高可用三大法宝

    SREcon 是由计算机科学领域知名机构USENIX主办,聚焦网站可靠性.系统工程.以及复杂分布式系统相关的运维行业技术盛会,今年SREcon17大会 Asia/Australia站于当地时间5月22 ...

  9. 58技术主席:还原万亿级三高业务场景的设计与实践

    孙玄,前58集团技术委员会主席,前转转二手交易平台首席架构师.今天想跟你聊点儿企业里那些年薪百万的架构师,他们的架构设计思维是如何升级的,我们来聊点儿干的! 01.百万年薪的核心竞争力 作为前58集团 ...

最新文章

  1. 在windows下安装python包管理器pip及使用
  2. 《软件设计师》——数据结构和算法基础
  3. Beetlsql自定义生成entity,mapper,md代码
  4. Yarn管理界面中Queue:root和Queue:default的区别
  5. 【APICloud系列|36】小米应用商店可以检测同个应用不同版本信息
  6. 插入排序:直接插入排序希尔排序
  7. 机器学习实战(MachineLearinginAction) 第一章
  8. 按创建日期删除指定日期之前的文件夹及文件夹下的所有子目录
  9. 算法---回溯法--模板解法
  10. Java-json系列(一):用GSON解析Json格式数据
  11. 非直接缓冲区与直接缓冲区
  12. nodejs实现同步http请求
  13. MSI GT60 16F4升级、超频、解锁功耗限制的研究
  14. python如何取消上一步操作的快捷键_ai返回上一步的快捷键是什么
  15. 写出一个程序,接受一个正浮点数值,输出该数值的近似整数值。如果小数点后数值大于等于5,向上取整;小于5,则向下取整。
  16. 毛毛虫 (树形dp)
  17. java实现小写转大写_人民币小写转大写(Java实现)
  18. 【Linux问题】Linux修改文件出现错误E45:“readonly” option is set(add ! to override)退出不了vim
  19. 交叉编译xorg-server
  20. OpenCV混合高斯模型前景分离

热门文章

  1. ObjectArx尺寸标注设置
  2. pycharm 使用matplotlib 绘图时图片不能显示中文
  3. Windows 与 Linux之间进行文件传输,文件传输工具Cuteftp的使用方法。
  4. 在国内市场,外汇到底能不能做?外汇合法吗?
  5. V4L2视频驱动框架---v4l2_device管理模块简述
  6. cadence——基本操作1
  7. 【医学图像分割】读论文系列 1
  8. 大三下的计划以及找工作的准备
  9. 只要掌握这两个方法便可快速学会怎么剪裁视频尺寸
  10. 07、人人都会设计模式:建造者模式--Builder