当前位置:   article > 正文

什么是CAS

什么是CAS

什么是CAS

CAS英文全称为Compare And Swap,比较并交换的意思。CAS操作包含3个参数-----V,E,N。V代表着要更新的变量(也可以理解为内存地址),E代表着预期原值,N代表着新值。CAS操作采用了乐观锁的思想,判断逻辑是只有在V的值等于E值时,才会将V的值设置为N。如果V的值和E值不想等,说明有别的线程先修改了E值,当前线程不做任何操作。到最后,CAS会返回当前V被其他线程修改过后的值。

分析AtomicInteger类的CAS操作

JDK的原子包java.util.concurrent.atomic提供了一堆的原子类,他们内部都是基于CAS算法实现的。现在选择其中之一的AtomicInteger类,分析它的方法 —— getAndIncrement() ,加深对CAS操作的印象。

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 {
            //获取value字段在内存中的地址相对于实例对象内存地址的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private volatile int value;
    
    //返回当前值 
    public final int getAndIncrement() {
      return unsafe.getAndAddInt(this, valueOffset, 1);
    }
  
    public final int getAndAddInt(Object var1, long var2, int var4) {
      int var5;
      do {
         //获得给定对象的指定偏移量offset的int值
         var5 = this.getIntVolatile(var1, var2);
         //一直进行CAS操作,直到成功
      } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

     return var5;
    } 
    
 
   // var1--操作的对象
   // var2--偏移量
   // var4--预期原值
   // var5--要修改的值 
   //比较并交换----本地方法,方法参数var1+var2就是具体的内存地址了
   public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
    
   //获取value值 
   public final int get() {
        return value;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

以上是getAndIncrement涉及的源码,接下来写段测试代码,来直观的感受下CAS操作:

测试代码如下:

public class TestMain {
    public static void main(String[] args) throws Exception {
        AtomicInteger atomicInteger = new AtomicInteger();
        int oldAtomicInteger1 = atomicInteger.getAndIncrement();
        System.out.println("第一次自增的旧值=" + oldAtomicInteger1);
        System.out.println("第一次自增的新值=" + atomicInteger.get());
        int oldAtomicInteger2 = atomicInteger.getAndIncrement();
        System.out.println("第二次自增的旧值=" + oldAtomicInteger2);
        System.out.println("第二次自增的新值=" + atomicInteger.get());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

运行结果如下:

在这里插入图片描述
从结果里可以知道,在调用方法getAndIncrement后,返回的是原值,同时atomicInteger对象完成了自增。

接下来打上断点,看看数据验证下想法:

第一次自增:
在这里插入图片描述
通过断点可以知道对象值var1为0,偏移量var2为12,这两个字段确定了内存地址,同时预期原值var5值为0
,要修改的值为var5+var4=1。根据CAS操作,要更新的变量值为0,等于预期原值,所以更新AtomicInteger对象的value为1。

继续断点:
在这里插入图片描述
可以看到在这步操作中,要更新的变量值已经变为了1,同时返回预期原值0。

符合第一次自增操作的结果:
在这里插入图片描述

继续第二步自增操作:
在这里插入图片描述
通过断点可以知道此时对象值var1为1,偏移量var2为12,这两个字段确定了内存地址,同时预期原值var5值为1
,要修改的值为var5+var4=2。根据CAS操作,要更新的变量值为1,等于预期原值,所以更新AtomicInteger对象的value为2。

继续断点:
在这里插入图片描述

可以看到在这步操作中,要更新的变量值已经变为了2,同时返回预期原值1。

符合第二次自增操作的结果:
在这里插入图片描述

总结

初步了解CAS操作后,我们可以发现,即使不使用锁,一样可以实现当前执行操作的线程对其他线程的排他性,实现原子性的操作,不过CAS也是有它自己的缺陷的,比如ABA问题和循环时间过长开销会很大等,但这并不影响我们对CAS操作逻辑的理解。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/311324
推荐阅读
相关标签
  

闽ICP备14008679号