什么是CAS?

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

先通过AtomicInteger来看一个Demo 

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

打印结果

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

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

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

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

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

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

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

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

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

再进入getAndAddInt()方法:

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

// 参数介绍,va1就是当前对象,var2就是偏移量,var4就是1
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    // 自旋操作
    do {
      // 通过当前对象和偏移量获取对应的内存值
      var5 = this.getIntVolatile(var1, var2);

      // 再一次通过var1(当前对象),var2(偏移量)获取内存里的值,然后与上一步获取的值var5做比较,如果相同,则更新内存里的值为var5 + 1,退出循环,如果不同,然后再一次循环,知道相同后更新退出
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

由此我们看出,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方法的作用首先检查当前引用是否等于预期引用,并且检查当前标志是否等于预期标志,如果都相等,则以原子方式将该引用和标志的值设为给定的更新值。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值