CAS原理

173 阅读2分钟

synchronized这种独占锁属于悲观锁,它是在假设一定会发生冲突的,那么加锁恰好有用,除此之外,还有乐观锁,乐观锁的含义就是假设没有发生冲突,那么我正好可以进行某项操作,如果要是发生冲突呢,那我就重试直到成功,乐观锁最常见的就是CAS

我们在读Concurrent包下的类的源码时,发现无论是ReenterLock内部的AQS,还是各种Atomic开头的原子类,内部都应用到了CAS,最常见的就是我们在并发编程时遇到的i++这种情况。传统的方法肯定是在方法上加上synchronized关键字:

public class Test {

    public volatile int i;

    public synchronized void add() {
        i++;
    }
}

但是这种方法在性能上可能会差一点,我们还可以使用AtomicInteger,就可以保证i原子的++了。

public class Test {

    public AtomicInteger i;

    public void add() {
        i.getAndIncrement();
    }
}

CAS源码分析

获取偏移量valueOffset,

public native long objectFieldOffset(Field var1);通过这个方法可以知道偏移量从jdk底层源码中获取。

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

然后再看看增加的方法

public final int getAndAdd(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta);
}
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;
}
我们看var5获取的是什么,通过调用unsafe的getIntVolatile(var1, var2),这是个native方法,具体实现到JDK源码里去看了,其实就是获取var1中,var2偏移量处的值。var1就是AtomicIntegervar2就是我们前面提到的valueOffset,这样我们就从内存里获取到现在valueOffset处的值了

compareAndSwapInt(var1, var2, var5, var5 + var4)换成compareAndSwapInt(obj, offset, expect, update)比较清楚,意思就是如果obj内的valueexpect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,如果这一步的CAS没有成功,那就采用自旋的方式继续进行CAS操作

private volatile int value;和unsafe.getAndAddInt(this, valueOffset, delta);
可以看出compareAndSwapInt(obj, offset, expect, update)中的obj为AtomicInteger类型,
AtomicInteger的value值为volatile类型,在看
 do {
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));这里是一个dowhile循环,如果obj内的value和expect不相等,
var5 = this.getIntVolatile(var1, var2);一直会
执行,即不断从内存中获取最新的值,来与obj内的value进行比较直到相等为止。从这个字段可以看出

CAS的缺点

  1. 只能保证对一个变量的原子性操作
  2. 长时间自旋会给CPU带来压力
  3. ABA问题