1、CAS是什么

CAS:Compare and swap,即比较并交换,它是一条CPU 并发原语。
它的功能时判断内存某个位置的值是否为预期值,如果是,则更改为新的值,这个过程是原子的。
CAS能够保证操作的原子性。
CAS可以看做是一种乐观锁。
一个CAS涉及到三个值以及以下操作:
三个核心参数:

  • 主内存中存放的共享变量的值:V(一般情况下,这个V是内存的地址值,可以通过这个地址获得内存中的值);
  • 工作内存中共享变量的副本值,也叫预期值:A;
  • 需要将共享变量更新成为的最新值:B。

涉及的操作:

  • 比较预期值A和主内存中共享变量的值是否相等。 (比较);
  • 如果比较相等,那么将想要修改为的新值B写入主内存的V,不想等则不写入。 (交换)
  • 操作成功返回true,失败返回false。

2、CAS的原理

CAS的原理主要涉及两个部分,一个是自旋锁,一个是Unsafe类。

2.1 Unsafe类

Unsafe类是jdk下自带的类,是CAS的核心类。由于Java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。
Unsafe类存在与sun.misc包中其内部方法操作可以像C的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法。
注意:Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务。

CAS并发愿意提现在Java语言中,就是sun.msic.Unsafe类中的各个方法,调用Unsafe类中的CAS方法,JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。再次强调,CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成,用于某个功能的一个过程;并且,原语的执行必须是连续的,在执行过程中不允许被中断,也就是说,CAS是一条CPU的原子指令,不会造成所谓的数据不一致。

2.2 自旋锁

2.3 CAS原理总结

比较并交换。比较当前工作内存中的值和主内存的值,如果相同,则执行规定的操作,否则只获取当前主内存的值,重新将主内存的值赋值给当前工作内存中的值,然后继续比较,直到主内存和工作内存中的值一致为止。

3、CAS的缺点

  • 循环时间长时,开销很大:

如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

  • 只能保证一个共享变量的原子操作:

当对一个共享变量执行操作时,可以使用循环CAS的方式来保证原子操作。但是,对于多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。

  • 引出ABA问题:

什么是ABA问题:
CAS算法实现的一个重要前提是,取出主内存中某时刻的数据,并在当下时刻比较并替换,那么在这个时间差内可能会存在数据发生变化。
比如说线程1从主内存V中取出a,这时另一个线程2也从主内存中取出,并且线程2进行了一些操作,将值a变成了b,然后线程2又将主内存V位置的数据变成a,这时线程1进行 CAS操作发现内存中仍然是a,然后线程1进行CAS操作成功。尽管线程1的CAS操作成功了,但是不代表这个过程就是没有问题的。
如何解决ABA问题:

  • 如果只考虑拿到的值相同就可以更改,那么可以忽略这个问题;
  • 添加版本号;
  • 从JDL5开始,atomic包提供了AtomicStampedReference来解决ABA问题,这个嘞的compareAndSet()方法的作用,首先是检查当前引用是否等于预期引用,并且当前标志(版本)是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。AtomicStampedReference通过其内部类Pair解决ABA问题:Pair中包含两个属性:版本号和引用,在compareAndSet()方法中,现对当前引用进行检查,再对版本号标志进行检查,只有全部相等才更新值。

4、CAS的应用