CAS的缺点

599 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

1.CAS保证并发性,但是需要多次比较

循环时间长,开销大,我们来看下核心方法getAndAddInt中的代码

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;
}

如果CAS操作没有成功(this.compareAndSwapInt(var1, var2, var5, var5 + var4))那么会返回false,这样由于加了!号,do while部分会一直尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的负担

没有加锁,允许多线程并发修改,但是需要多次比较

2.只能保证一个共享变量的原子操作。

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。还有一个方法就是把多个共享变量封装进一个对象,然后通过AtomicReference类保证引用对象之间的原子性。(从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性)

3.ABA问题(重点)

t2线程将A改成了B,并且把工作内存中的B写回了主物理内存,但是t2线程又把工作内存中的值改成了A,写回主物理内存,此时主物理内存中的值还是B,此时过了10s,t1线程开始比较期望值和实际值发现没有别改变,CAS操作成功。但是实际上主物理内存中的值是被修改过的。

image.png

开始和结束是一样的,但是中间数据可能被修改了很多次

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

比如说一个线程T1从内存位置V取出A,这时候另一个线程T2也从内存中取出A,并且线程T2进行了一些操作后又将V位置的数据变成A,这时候线程T1进行CAS操作发现内存中仍然是A,然后线程T1操作成功 尽管线程T1进行CAS操作成功,但是不代表这个过程没有问题。