什么是CAS机制

CAS机制

CAS是Compare-And-Swap的缩写,即比较和交换,比较和交换是原子操作,通过CPU指令层面保证了其原子性。CAS通过将预期的值和实际的值进行比较,如果预期的值和实际的值一样,则认为实际值没有发生变化,此时可以将实际值设置为新值。下面通过一段Java代码来演示一下CAS的使用:

java 复制代码
import sun.misc.Unsafe;
import java.lang.reflect.Field;

class Counter {

    private volatile int i = 0;

    /**
     * Unsafe 是 JDK封装的一个内部使用类,需要使用反射的方式来创建其对象
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    private Unsafe getUnsafe() throws NoSuchFieldException, IllegalAccessException {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        return (Unsafe) field.get(null);
    }

    void casSetValue(int expected, int newValue) throws NoSuchFieldException, IllegalAccessException {
        Unsafe unsafe = getUnsafe();
        long offset = unsafe.objectFieldOffset(Counter.class.getDeclaredField("i"));
        unsafe.compareAndSwapInt(this, offset, expected, newValue);
    }

    int getValue() {
        return i;
    }
}

public class CasDemo01 {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Counter counter = new Counter();
        // 这里预期值是2,但实际值是0,新的值会设置失败
        counter.casSetValue(2, 1);
        // 输出的结果为: 0
        System.out.println(counter.getValue());
        // 这里预期值是0,实际值也是0,新的值设置成功
        counter.casSetValue(0, 2);
         // 输出的结果为: 2
        System.out.println(counter.getValue());
    }

}

上面演示了Java代码中如何使用CAS机制,Java提供了Unsafe类,其中有提供三个实现CAS机制的核心方法,分别对不同类型的变量进行修改。

java 复制代码
//用于整型变量的原子操作
boolean compareAndSwapInt(Object o, long offset, int expected, int x)
//用于长整型变量的原子操作
boolean compareAndSwapLong(Object o, long offset, long expected, long x)
//用于对象引用的原子操作,不涉及对象的属性
boolean compareAndSwapObject(Object o, long offset, Object expected, Object x)

这些方法都调用了Java本地方法,由JVM提供底层实现。现代的CPU架构中,这些操作通常会被编译为特定的CPU指令。在x86架构中,对应的是CMPXCHG,它可以原子性的完成比较和交换操作,确保在多线程环境下的线程安全性。

这种硬件级别的支持使得CAS非常高效:

  • 由CAS实现的同步,避免了synchronized锁带来的上下文切换
  • 实现了真正的无锁编程
  • 在竞争不激烈的情况下,性能显著优于传统的同步机制(因为一旦竞争过于激烈,会存在CAS一直失败的情况,反而性能有限)。

ABA问题

如果有一个整型值X,一开始实际值是1,然后被修改为2,最后又恢复成1,这个时候我们使用compareAndSwapInt(Object o, long offset, int 1, int 3)来修改这个X的时候,虽然修改可以成功,看起来没什么问题,但是CAS无法感知这中间的变化,可能导致业务逻辑问题。

要解决这个问题,需要为要修改的值提供一个版本号。下面是Java的concurrent包中的一个实现:

java 复制代码
import java.util.concurrent.atomic.AtomicStampedReference;
public class AbaProblem {
    public static void main(String[] args) {
        // 初始化版本号号是0
        AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(1, 0);
        Integer stamp = ref.getStamp();
        // 输出 0
        System.out.println("版本号是:" + stamp);
        ref.compareAndSet(1, 3, stamp, stamp + 1);
        // 输出 1
        System.out.println("新的版本号是:" + ref.getStamp());
    }
}

查看源码发现,AtomicStampedReference定义了一个私有内部类Pair,使用Pair存储值和版本号。在 ref.compareAndSet中创建一个新的Pair对象,并通过compareAndSwapObject()方法更新AtomicStampedReference中对Pair对象的引用,可以起到同时原子更新两个变量的效果(值和版本号)。

java 复制代码
 private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
}

ref.compareAndSet的源码如下

java 复制代码
// Pair.of 创建新的Pair对象
// casPair方法,类似上面的compareAndSwapObject方法,原子化更新对象的引用
public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference &&
            expectedStamp == current.stamp &&
            ((newReference == current.reference &&
              newStamp == current.stamp) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }
相关推荐
南部余额17 小时前
Maven 依赖管理与版本优化
java·maven
create1717 小时前
IntelliJ IDEA 等软件如何与 AI 编程工具(Cursor、Windsurf、Qoder等)实现互相跳转
java·ide·人工智能·intellij-idea
望获linux17 小时前
论文解读:利用中断隔离技术的 Linux 亚微秒响应性能优化
java·linux·运维·前端·arm开发·数据库·性能优化
龙茶清欢18 小时前
7、微服务中 DTO、VO、PO、BO 的设计规范
java·spring boot·spring cloud
ToneChip18 小时前
配合AI自动生成时序图--最详细在VS Code中使用PlantUML绘图
java
苏纪云18 小时前
算法<java>——排序(冒泡、插入、选择、归并、快速、计数、堆、桶、基数)
java·开发语言·算法
xie_pin_an18 小时前
SpringBoot 统一功能处理:拦截器、统一返回与异常处理
java·spring boot·后端
冬天vs不冷18 小时前
Java基础(十三):内部类详解
android·java·python
YQ_ZJH18 小时前
Java List列表创建方法大总结
java·开发语言·数据结构·算法·list
城管不管18 小时前
Spring + Spring MVC + MyBatis
java·spring·mvc