CAS
操作 ABA
问题及解决方案
注意: 本文是博主自己的拙见,如发现描述或理解问题,可以联系博主随时修改。
在上一篇文章中 《一篇文章看懂CAS》 描述到了 CAS
的工作原理,那么这篇文章来说一下 CAS
存在的 ABA
问题及如何解决
什么是 ABA
问题 ?
主内存中变量为 10
,正常线程 t1
线程和错误线程 t2
线程同时 -5
那么 t1
和 t2
期望的值都是 5
,正常情况下只有 t1
才会正确执行,t2
会更新失败
- 当线程
t1
,t2
同时启动,并将主内存中的变量拷贝到自己工作空间完成了递减操作,假设t2
线程在此时进入了阻塞状态,t1
更新成功后返回
- 此时 新进入一条线程
t3
要对主内存中变量+5
操作, 而t2
线程依然处于阻塞状态,主内存中的变量变成了10
t3
执行完毕后t2
线程恢复,去更新主内存中的内容,发现旧值和主内存中的值一致,于是将主内存中的值更新为5
根据上面的情况可以看出,如果没有错误线程t2
的更新成功,那么正确值应该是10
,但是由于t2
更新成功了,导致最终结果为5
,由此可见,如果在现实生活中发生这样的问题,是不可行的,对于上面这种情况又该如何解决。
ABA
问题如何避免 ?
通过上述对 ABA
问题的阐述,发现最终的问题在于不同线程在更新变量的时候操作的都是不同版本的数据,那么我们是不是可以直接新增一个数据的版本号对数据进行控制,在更新数据之前对比一下版本号是否一致,就能保证数据准确性了。没错,思路就是这样子的。在 JDK
中提供了基于版本号的原子操作类
public class CasVersionAbaDemo {
static AtomicReference<Integer> a = new AtomicReference<>(0);
public static synchronized void increment (){
a.compareAndSet(a.get(), a.get() + 1);
}
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
new Thread(CasVersionAbaDemo::increment).start();
}
Thread.sleep(4000);
System.out.println(a.get());
}
}
参考:什么是CAS机制?