CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。

上篇文章讲到CAS会出现一个ABA问题。那什么是ABA问题呢?

官方一点的解释就是:当有多个线程对一个原子类进行操作的时候,某个线程在短时间内将原子类的值A修改为B,又马上将其修改为A,此时其他线程不感知,还是会修改成功。

代码案例:

//线程操作资源,原子类ai的初始值为4
static AtomicInteger ai = new AtomicInteger(4);
public static void main(String[] args) {
new Thread(() -> {
//利用CAS将ai的值改成5
boolean b = ai.compareAndSet(4, 5);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为5:"+b);
//休眠一秒
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
//利用CAS将ai的值改回4
b = ai.compareAndSet(5,4);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为4:"+b);
},"A").start();
new Thread(() -> {
//模拟此线程执行较慢的情况
try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
//利用CAS将ai的值从4改为10
boolean b = ai.compareAndSet(4, 10);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为10:"+b);
},"B").start(); //等待其他线程完成,为什么是2,因为一个是main线程,一个是后台的GC线程
while (Thread.activeCount() > 2) {
Thread.yield();
} System.out.println("ai最终的值为:"+ai.get());
}

 

执行结果:

可以看到,线程B最终是将ai的值修改成功了。

上面例子模拟的是A、B两个线程操作一个资源ai,A的执行速度比B的快,在B执行前,A就已经将ai的值改为5之后马上又把ai的值改回为4,但是B不感知,所以最后B就修改成功了。

比如有两个单身狗A、B,A在某个时间段内找到女朋友但是又分开了,但是没告诉B,此时B还是会在A是单身狗的情况下带A去打游戏。

ABA问题的解决方案?

数据库有个锁称为乐观锁,是一种基于数据版本实现数据同步的机制,每次修改一次数据,版本就会进行累加。

同样,Java也提供了相应的原子引用类AtomicStampedReference<V>

上图中的初始邮票就是版本号。

根据之前的代码改动的例子:

static AtomicStampedReference<Integer> ai = new AtomicStampedReference<>(4,0);
public static void main(String[] args) {
new Thread(() -> {
//四个参数分别是预估内存值,更新值,预估版本号,初始版本号
//只有当预估内存值==实际内存值相等并且预估版本号==实际版本号,才会进行修改
boolean b = ai.compareAndSet(4, 5,0,1);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为5:"+b);
try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}
b = ai.compareAndSet(5,4,1,2);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为4:"+b);
},"A").start();
new Thread(() -> {
try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}
boolean b = ai.compareAndSet(4, 10,0,1);
System.out.println(Thread.currentThread().getName()+"是否成功将ai的值修改为10:"+b);
},"B").start(); while (Thread.activeCount() > 2) {
Thread.yield();
} System.out.println("ai最终的值为:"+asri.getReference());
}

  

运行结果:

可以看到,最终B并没有成功修改ai的值

=======================================================

我是Liusy,一个喜欢健身的程序员。

欢迎关注公众号【Liusy01】,一起交流Java技术及健身,获取更多干货。



CAS导致的ABA问题以及解决方案的更多相关文章

  1. CAS导致的ABA问题及解决:时间戳原子引用AtomicReference、AtomicStampedReference

    1.CAS导致ABA问题: CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差中会导致数据的变化. 比如:线程1从内存位置V中取出A,这时线程2也从V中取出A ...

  2. 基于CAS分析对ABA问题的一点思考

    基于CAS分析对ABA问题的一点思考 什么是CAS? 背景 synchronized加锁消耗太大 volatile只保证可见性,不保证原子性 基础 用CPU提供的特殊指令,可以: 自动更新共享数据; ...

  3. CAS如何解决ABA问题

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. CAS如何解决ABA问题 什么是ABA:在CAS过程中,线程1.线程2分 ...

  4. jqueryeasyUI dialog 弹出窗口超出浏览器,导致不能关闭的bug解决方案

    jqueryeasyUI dialog 弹出窗口超出浏览器,导致不能关闭的bug解决方案 2014年8月30日 3233次浏览 相信很多前端朋友都用过jqueryeasyUI,jqueryeasyUI ...

  5. VS·调试过程中某个操作导致调试突然退出之解决方案

    阅文时长 | 0.11分钟 字数统计 | 232字符 主要内容 | 1.引言&背景 2.声明与参考资料 『VS·调试过程中某个操作导致调试突然退出之解决方案』 编写人 | SCscHero 编 ...

  6. AtomicStampedReference AtomicReference解决CAS机制中ABA问题

    AtomicStampedReference AtomicReference解决CAS机制中ABA问题 AtomicStampedReference AtomicStampedReference它内部 ...

  7. CAS -- ABA问题的解决方案

    我们现在来说什么是ABA问题.假设内存中有一个值为A的变量,存储在地址V中. 此时有三个线程想使用CAS的方式更新这个变量的值,每个线程的执行时间有略微偏差.线程1和线程2已经获取当前值,线程3还未获 ...

  8. 并发中的Native方法,CAS操作与ABA问题

    Native方法,Unsafe与CAS操作 >>JNI和Native方法 Java中,通过JNI(Java Native Interface,java本地接口)来实现本地化,访问操作系统底 ...

  9. 关于CAS中的ABA问题存在的隐患

    一开始觉得ABA问题确实是个问题,但是具体场景是什么呢,虽然过程改了,但是结果一样,取的值也一样 不明所以,不过呢,这里也有一个小的demo可以说明一下 该例子通过AtomicReference判断对 ...

随机推荐

  1. 动态数组java实现

    数组是一种顺序存储的线性表,所有元素的内存地址是连续的. 动态数组相对于一般数组的优势是可以灵活地添加或删除元素.而一般数组则受限于固定的内存空间.只能有限的添加元素 动态数组(Dynamic Arr ...

  2. dpkg:处理 xxx (--configure)时出错解决办法

    1.$ sudo mv /var/lib/dpkg/info /var/lib/dpkg/info_old //现将info文件夹更名2.$ sudo mkdir /var/lib/dpkg/info ...

  3. 记一次因为Gradle与Lombok不兼容导致编译时的内存溢出 Expiring Daemon because JVM heap space is exhausted

    1.现象 版本 Gradel:6.1.1 / 6.5.1 Lombok:1.8.6 / 1.8.10 截图 解决过程 调大idea的堆内存 不行 × idea安装目录中找到 idea64.exe.vm ...

  4. jenkins,开源CI工具

    目前最热门CI工具的jenkins,学习笔记: 一.jenkins如何实现执行命令 1.执行jenkins同主机上的命令

  5. linux系统学习

    一.linux系统如何配置环境变量 1.在Windows 系统下,很多软件安装都需要配置环境变量,比如 安装 jdk ,如果不配置环境变量,在非软件安装的目录下运行javac 命令,将会报告找不到文件 ...

  6. Web自动化必会知识:「Web基础、元素定位、元素操作、Selenium运行原理、项目实战+框架」

    1.web 基础-html.dom 对象.js 基本语法 Dom 对象里面涉及元素定位以及对元素的修改.因为对元素操作当中涉及的一些 js 操作,js 基本语法要会用.得要掌握前端的基本用法.为什么要 ...

  7. Java数据结构——根据遍历结果构造二叉树

    一.已知前序.中序.后序遍历结果的其中两种,还原二叉树. ①已知前序遍历结果:1,2,4,5,3,6,7 中序遍历结果:4,2,5,1,6,3,7还原二叉树后BFS出结果. TreeNode.java ...

  8. 什么是谷歌PageRank (简称PR值)

    http://www.wocaoseo.com/thread-213-1-1.html 谷歌pageRank是谷歌用来评测网页质量高低的一个工具,主要分为0到10共11个等级,目前有很多的工具或谷歌工 ...

  9. Appium框架

    1.通过TestNG单元测试框架管理/执行测试用例 2.TestNG参数化,App启动的四个配置存到Testng.xml中 3.通过page object (PO)设计模式,测试用例和页面元素分离,页 ...

  10. python练习 数字不同数之和+人名最多数统计

    数字不同数之和 描述 获得用户输入的一个整数N,输出N中所出现不同数字的和.‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‭‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬ ...