什么是CAS?以及CAS的底层原理

CAS(Compare and Swap) 是JUC中的并发编程中常用的原子操作,它用于实现多线程环境下的无锁同步,CAS操作包含三个操作数 : 内存位置(或称为变量的地址)、期望值和新值。

CAS的执行过程如下:

  1. 读取内存位置的当前值,也就是我们的期望值。
  2. 比较期望值与内存位置的当前值是否相等。
  3. 如果相等,将新值写入内存位置;如果不相等,则说明其他线程已经修改了内存位置的值,CAS操作失败。
  4. CAS操作返回当前内存位置的值。

CAS的操作是原子性的,能够保证在多线程环境下对共享变量的原子性操作,如果CAS操作失败,通常会进行重试,直到成功为止。

public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(666);
        // 获取真实值,并替换为相应的值
        boolean b = atomicInteger.compareAndSet(666, 2019);
        System.out.println(b); // true
        boolean b1 = atomicInteger.compareAndSet(666, 2020);
        System.out.println(b1); // false
        atomicInteger.getAndIncrement();
    }
}

Unsafe类

Unsafe 是 CAS 的核心类,由于 Java 方法无法直接访问底层系统,而需要通过本地(native)方法来访问, Unsafe 类相当一个后门,基于该类可以直接操作特定内存的数据。Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像 C 指针一样直接操作内存,因为 Java 中 CAS 操作执行依赖于 Unsafe 类。

CAS 并发原体现在 JAVA 语言中就是 sun.misc.Unsafe 类中的各个方法。调用 UnSafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令。这是一种完全依赖硬件的功能,通过它实现了原子操作。

由于 CAS 是一种系统源语,源语属于操作系统用语范畴,是由若干条指令组成,用于完成某一个功能的过程,并且原语的执行必须是连续的,在执行的过程中不允许被中断,也就是说 CAS 是一条原子指令,不会造成所谓的数据不一致的问题。它允许Java程序直接访问和修改内存、执行CAS操作等底层操作,但是它的使用需要谨慎,因为它绕过了Java语言的一些安全检查和限制。

Unsafe类的使用需要具有高度的专业知识和经验,因为它可以导致一些潜在的安全问题和内存错误。因此,一般情况下,不建议直接使用Unsafe类,而是通过高级的并发工具和原子类来实现线程安全的操作。

CAS的缺点

  • 循环时间长开销很大
  • 如果 CAS 失败,会一直尝试,如果 CAS 长时间一直不成功,可能会给 CPU 带来很大的开销(比如线程数很多,每次比较都是失败,就会一直循环),所以希望是线程数比较小的场景。
  • 只能保证一个共享变量的原子操作
  • 对于多个共享变量操作时,循环 CAS 就无法保证操作的原子性。
  • 引出 ABA 问题