CAS -- ABA问题的解决方案

我们现在来说什么是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,但是版本号不相等,所以这一次更新失败。

在Java中,AtomicStampedReference类就实现了用版本号作比较额CAS机制。

1. java语言CAS底层如何实现?

利用unsafe提供的原子性操作方法(我也不太清楚???)。

2.什么事ABA问题?怎么解决?

当一个值从A变成B,又更新回A,普通CAS机制会误判通过检测。

利用版本号比较可以有效解决ABA问题。

参考文档:https://blog.csdn.net/qq_32998153/article/details/79529704

原文地址:https://www.cnblogs.com/william-dai/p/10890324.html

时间: 2024-08-29 21:42:56

CAS -- ABA问题的解决方案的相关文章

CAS / ABA

CAS / ABA 标签(空格分隔): 操作系统 1. CAS 解决 Volatile 不保证原子性的问题 /** * Atomically increments by one the current value. * * @return the previous value */ public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } public final int ge

Java并发编程原理与实战四十三:CAS ---- ABA问题

CAS(Compare And Swap)导致的ABA问题 问题描述 多线程情况下,每个线程使用CAS操作欲将数据A修改成B,当然我们只希望只有一个线程能够正确的修改数据,并且只修改一次.当并发的时候,其中一个线程已经将A成功的改成了B,但是在线程并发调度过程中尚未被调度,在这个期间,另外一个线程(不在并发中的请求线程)将B又修改成了A,那么原来并发中的线程又可以通过CAS操作将A改成B 测试用例: public class AbaPro { private static final Rando

CAS ABA问题

接触并发编程少不了CAS,这里不讲CAS,在另一篇文章里面有写CAS,这里只关注CAS的ABA问题. 什么叫CAS的ABA问题? 因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了. 比如线程A操作值a,将值修改为b,这时线程B也拿到了a的值,将a改为b,再改为a,这时线程A比对值的时候,发送值还是和expect的一样,就会继续操作.如下面代码,T2的CAS

Java CAS ABA问题发生的场景分析

提到了CAS操作存在问题,就是在CAS之前A变成B又变回A,CAS还是能够设置成功的,什么场景下会出现这个问题呢?查了一些资料,发现在下面的两种情况下会出现ABA问题. 1.A最开始的内存地址是X,然后失效了,有分配了B,恰好内存地址是X,这时候通过CAS操作,却设置成功了 这种情况在带有GC的语言中,这种情况是不可能发生的,为什么呢?拿JAVA举例,在执行CAS操作时,A,B对象肯定生命周期内,GC不可能将其释放,那么A指向的内存是不会被释放的,B也就不可能分配到与A相同的内存地址,CAS失败

volatile | CAS| ABA

JMM JMM(Java内存模型Java Memory Model)是一种抽象的概念并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段.静态字段和构成数组对象的元素)的访问方式. JMM关于同步的规定: ①线程解锁前,必须把共享变量的值刷新回主内存: ②线程解锁前,必须读取主内存的最新值到自己的工作内存: ③加锁解锁是同一把锁: ①把变量读到各个线程的工作内存中:②线程运算完之后把变量的值改好,然后把它写回主内存:③可见性--让其他线程马上知道这个值: 各个线

CAS与ABA问题产生和解决

乐观锁和悲观锁 Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守.CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新. 性能对比: Synchronized关键字会让没有得到锁资源的线程进入blocked状态,而后在争夺到锁资源后恢复为runnable状态,这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高. 尽管Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重

Java高性能编程之CAS与ABA及解决方法

Java高性能编程之CAS与ABA及解决方法 前言 如果喜欢暗色调的界面或者想换换界面,可以看看我在个人博客发布的 Java高性能编程之CAS与ABA及解决方法. CAS概念 CAS,全称Compare And Swap,比较与交换. 属于硬件级别的同步原语,从处理器层面提供了内存操作的原子性. 从概念上,我们可以得出三点.第一,CAS的运作方式(通过比较与交换实现).第二,硬件层面支持,性能肯定不低(当然它也不是银弹).第三,提供原子性,那么它的功能肯定是确保原子性,从而确保线程安全. 实际使

CAS(Compare And Swap)分析

CAS(Compare And Swap)指的是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令.这个指令会对内存中的共享数据做原子的读写操作. 简单介绍一下这个指令的操作过程:首先,CPU会将内存中将要被更改的数据与期望的值做比较.当这两个值相等时,CPU才会将内存中的数值替换为新的值,否则便不做操作.最后,CPU 会将当前变量的真实值返回.这一系列的操作是原子的. CAS是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它.而synchronized是一种悲观锁,它认

【死磕Java并发】----深入分析CAS

CAS,Compare And Swap,即比较并交换.Doug lea大神在同步组件中大量使用CAS技术鬼斧神工地实现了Java多线程的并发操作.整个AQS同步组件.Atomic原子类操作等等都是以CAS实现的,甚至ConcurrentHashMap在1.8的版本中也调整为了CAS+Synchronized.可以说CAS是整个JUC的基石. CAS分析 在CAS中有三个参数:内存值V.旧的预期值A.要更新的值B,当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干.