CAS底层原理

646 阅读3分钟

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

源码分析

CAS的底层原理是基于自旋操作和Unsafe类的

class MyTask2 {
    static AtomicInteger num = new AtomicInteger(5);

    public static void main(String[] args) {
        num.getAndIncrement();
    }
}

先来看下getAndIncrement方法的源码

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
/**
 * Atomically increments by one the current value.
 *
 * @return the previous value
 */
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
}

可以看到返回的是unsafe类的实例调用的getAndAddInt方法

其中

  • this:当前对象
  • valueOffset:内存偏移量,Unsafe根据内存偏移地址获取数据
  • 1:表示每次增加的值

再点击进去发现我们来到了Unsafe类,其中的很多方法都是用native修饰的,说明Unsafe类中的方法都直接调用操作系统底层资源执行响应的任务

public native void park(boolean var1, long var2);

public native int getLoadAverage(double[] var1, int var2);

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

主要看下do while循环的过程

  • 首先var5 = this.getIntVolatile(var1, var2);中var1是当前变量,var2表示内存的偏移量
  • while中的compareAndSwapInt尝试修改var5的值,该方法通过var1和var5获取变量对的值,如果这个值和var5不一样,说明已经有其他线程修改了var1+var2地址处的值,此时该方法返回false由于前面有个!号就继续循环。
  • 如果这个值和var5一样,说明没有被其他线程修改,可以将var1+var2处的值修改为var5+var4,返回true直接退出循环

由于compareAndSwapInt是原子操作,所以修改var1+var2地址处的值的时候可以保证线程安全,

可以看到没有修改成功就一直在while循环中一直尝试,这就是自选锁的实现思想,相比于synchronized它在保障线程安全性的同时还保证了并发性,是线程不断尝试而不是锁住一块代码不让其他线程进来。

Unsafe

它是CAS的核心类,我们知道Java方法是无法访问底层系统,需要通过本地(native)方法访问,Unsafe类相当于提供了一个后门,通过这个类可以直接操作特定内存的数据。

再来看下Unsafe类的位置

package sun.misc;

public final class Unsafe {
}

可以看到它位于sum.misc包中,内部的方法可以像C语言的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

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

    private volatile int value;

变量value用volatile修饰,保证了多线程情况下数据的可见性。

CAS的全称为Compare-And-Swap,它是一条CPU并发原语,可以判断内存某个位置的值是否为预期值,如果是则更改为新值,这个过程是原子的

CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们是实现出CAS汇编指令。这是一种完全依赖于硬件的操作,通过它实现原子操作。

原语

由于CAS是一种系统原语,属于操作系统用语的范畴,是由若干条指令组成,用于完成某个功能的过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是是CAS是一条CPU的原子指令,不会造成数据不一致的问题。