当前位置:   article > 正文

什么是CAS?

什么是cas

CAS是compare and swap的缩写,中文翻译为比较并交换。

先通过AtomicInteger来看一个Demo 

  1. public class Demo {
  2. public static void main(String[] args) {
  3. // 原子类 不传入参数5,默认为0
  4. AtomicInteger atomicInteger = new AtomicInteger(5);
  5. // compareAndSet就是一个cas操作,这个方法的意思就是期望现在内存的值还是5,如果是,就改为2019,然后返回true
  6. boolean b1 = atomicInteger.compareAndSet(5, 2019);
  7. System.out.println(b1 + " current data " + atomicInteger.get());
  8. // 这里由于在上一步以及将内存的值改为了2019,所以更新失败,无法修改为2020,返回false
  9. boolean b2 = atomicInteger.compareAndSet(5, 2020);
  10. System.out.println(b2 + " current data " + atomicInteger.get());
  11. }
  12. }

打印结果

 CAS 操作包含三个操作数 —— 内存值(V)、预期原值(A)和新值(B)

通过内存值与预期原值的比较,如果相等,则能将内存值修改为新值。

如果不想等,说明内存值已经被别的线程修改过了,不做处理。

CAS底层是通过调用UnSafe类中的CAS方法去实现的。

下面通过AtomicInteger的getAndIncrement()方法具体来看一下怎么实现的:

首先我们需要知道 AtomicInteger里面两个关键变量:

  • ValueOffset它是AtomicInteger对象内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的
  • value:AtomicInteger具体存入的值,被volatile修饰,保证了多线程之间的可见性.

先看getAndIncrement()方法,调用的是unsafe的getAndAddInt()方法:

  1. public final int getAndIncrement() {
  2. // this为当前对象,valueOffset偏移量(内存地址)
  3. return unsafe.getAndAddInt(this, valueOffset, 1);
  4. }

再进入getAndAddInt()方法:

这个方法通过自旋操作,与CAS比较与交换,当然还要记得内存值需要有volatile修饰成为可见的(这里的内存之就是AtomicInteger的value变量,已经修饰为volatile),确保了原子性。所以i++操作换成AtomicInteger的getAndIncrement()方法就能保住原子性。

  1. // 参数介绍,va1就是当前对象,var2就是偏移量,var4就是1
  2. public final int getAndAddInt(Object var1, long var2, int var4) {
  3. int var5;
  4. // 自旋操作
  5. do {
  6. // 通过当前对象和偏移量获取对应的内存值
  7. var5 = this.getIntVolatile(var1, var2);
  8. // 再一次通过var1(当前对象),var2(偏移量)获取内存里的值,然后与上一步获取的值var5做比较,如果相同,则更新内存里的值为var5 + 1,退出循环,如果不同,然后再一次循环,知道相同后更新退出
  9. } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
  10. return var5;
  11. }

由此我们看出,CAS并没有像Synchronized那样加锁,由此他效率肯定是要高的,更加轻量级。

但是它也是有缺点的:

1.由于是自旋操作,如果长时间更新不成功,那么就会一直在那里无限循环,对CPU的消耗很大。

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

3.ABA问题,因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

从jdk1.5开始,jdk中的Atomic包里提供了一个类AtomicStampedReference来解决ABA问题。这个类的compareAndSet方法的作用首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果都相等,则以原子方式将该引用和标志的值设为给定的更新值。
 

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

闽ICP备14008679号