深入理解 CAS 及 ABA问题

CAS,Compare And Swap,即比较并交换。

CAS是Atomic 包的核心,更是整个J.U.C 包的 基石。


本文 将通过AtomicInteger这个类,来分析是如何通过CAS来保证 Atomic的原子性的。

直接进入 AtomicInteger 这个类,可以看到


AtomicInteger 中定义了一个变量value并且用 valatile来修饰的, 还有个静态代码块 的 valueOffset变量可以得到 这个value的值。

所以我们在初始化 AtomicInteger 这个类,并设值后,调用 AtomicInteger  的其它方法,就是用的这个 valueOffset来接收了

然后进入 AtomicInteger 的 incrementAndGet 方法,该方法是 默认 +1 操作。

public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

this 当前类,没什么好说的,

    valueOffset ,就是此时 AtomicInteger 类保存的 value 值。

    进入 该方法的实现

 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);   //从主存中 去拿这个变量最新的值
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

分析如下:如果当前value 的值是 5,那么进行 5 + 1 这个操作

var1 是当前的AtomicInteger 这个类

var2 是value 当前的值 5 ,var 4是准备要加 1

var4 是当前准备从内存去取得最新的 AtomicInteger 的 value值,

然后进入 compareAndSwapInt 方法 ,这就是我们说的 CAS了

var2是 预期的值 ,var5 是主存中的值, var5+var4是要更新的结果,

当且只当 var2与v5相等时,才会返回更新的结果值。

因为v2的值是当前线程中工作内存的值,只有确认自己读到的值跟主存中的值一致的时候,才能去修改这个值。

在多线程竞争的情况下,如果var2主存中的值 被其它线程修改成3了,

那var2 等于2 的时候 就不等于 主存中的3 ,那么就不会进行相加。


但是使用Atomic这个类 会有一个ABA的问题。

CAS需要检查操作值有没有发生改变,如果没有发生改变则更新。但是存在这样一种情况:如果一个值原来是A,变成了B,然后又变成了A,那么在CAS检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。对于ABA问题其解决方案是加上版本号,即在每个变量都加上一个版本号,每次改变时加1,即A —> B —> A,变成1A —> 2B —> 3A。

如何解决呢?

Java提供了AtomicStampedReference这个类来解决。

AtomicStampedReference通过版本号stamp,从而避免ABA问题。

compareAndSet有四个参数,分别表示:预期引用、更新后的引用、预期标志、更新后的标志

猜你喜欢

转载自blog.csdn.net/world6/article/details/80393566
今日推荐