java的cas

53 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第9天,点击查看活动详情

CAS 是什么?

  • CAS:比较并交换compareAndSet,它是一条 CPU 并发原语,它的功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子性的。

  • 例: AtomicInteger 的 compareAndSet(‘期望值’,’设置值’) 方法,期望值与目标值一致时,修改目标变量为设置值,期望值与目标值不一致时,返回 false 和最新主存的变量值

  • CAS 的底层原理

    例: AtomicInteger.getAndIncrement() 调用 Unsafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令 这是一种完全依赖于硬件的功能,通过它实现原子操作。 原语的执行必须是连续的,在执行过程中不允许被中断,CAS 是 CPU 的一条原子指令。

  • CAS的思想就是乐观锁的思想

AtomicInteger

在JUC并发包中,CAS和AtomicInteger(原子类的value值都被volatile修饰了)一起保证了并发安全。下面我们以AtomicInteger.getAndIncrement() 方法讲一下

image.png

AtomicInteger里有如下方法:

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

方法解释: unsafe: rt.jar/sun/misc/Unsafe.class

  • Unsafe 是 CAS 的核心类,由于 Java 无法直接访问底层系统,需要通过本地方法来访问 Unsafe 相当于一个后门,基于该类可以直接操作特定内存的数据 Unsafe 其内部方法都是 native 修饰的,可以像 C 的指针一样直接操作内存 Java 中的 CAS 操作执行依赖于 Unsafe 的方法,直接调用操作系统底层资源执行程序
  • this: 当前对象 变量 value 由 volatile 修饰,保证了多线程之间的内存可见性、禁止重排序
  • valueOffset: 内存地址表示该变量值在内存中的偏移地址,因为 Unsafe 就是根据内存偏移地址获取数据
  • 1: 固定写死,原值加1
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;
}
  • Unsafe.getAndAddInt()
  • getIntVolatile: 通过内存地址去主存中取对应数据
  • while(!this.compareAndSwapInt(var1,var2,var5,var5 + var4)):
  • 将本地 value 与主存中取出的数据对比,如果相同,对其作运算, *此时返回 true,取反后 while 结束,返回最终值。 *如果不相同,此时返回 false,取反后 while 循环继续运行,此时为自旋锁<重复尝试> *由于 value 是被 volatile 修饰的,所以拿到主存中最新值,再循环直至成功。