CAS原理

CAS是什么?

CAS是比较并交换的意思,其全称为Compare-And-Swap,简称为CAS。
实际上指的是原子类AtomicReference中的compareAndSet(V expect, V update)这个方法。
其源码为:

	/**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(V expect, V update) {
        return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
    }

expect指的是主内存的期望值,update为主内存的更新值。
当工作内存将更新值写回到主内存的时候,会将主内存的值跟期望值进行比较,当主内存的值与期望值一致时,才会将主内存的值更新为更新值;否则不会更新,工作内存会重新获取主内存的值。

Unsafe类:

通过源码可以看出compareAndSet(V expect, V update)这个方法的实现调用的是unsafe类的compareAndSwapObject(this, valueOffset, expect, update)方法。

this参数指的是当前对象。
valueOffset参数表示该变量值在内存中的偏移地址,即可以根据内存地址操作变量值。
expect参数是主内存的期望值。
update参数是需要更新的值。

其实unsafe类就是根据内存偏移地址获取数据的。
当点击进入compareAndSwapObject这个方法可以看到的源码:

    public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);

    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

    public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

底层是一个native方法,调用的是C语言代码。

从上面源码可以看出unsafe类是实现CAS的核心类,由于Java方法无法直接访问底层系统,所以需要通过本地(native)方法来访问。
unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。
unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,Java中的CAS操作是依赖于unsafe类的方法。

CAS的底层原理:

CAS是一条CPU的并发原语。
其功能是判断内存某个位置的值是否为预期值,如果是则更改为新的值,这个过程是原子的。

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

由于CAS是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题

分析AtomicInteger类中的自增方法

AtomicInteger类中的getAndIncrement()方法:

	private volatile int value;
    /**
     * Creates a new AtomicInteger with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }
    /**
     * Creates a new AtomicInteger with initial value {@code 0}.
     */
    public AtomicInteger() {
    }
	/**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

Unsafe类中的getAndAddInt方法:

	public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

首先明确getAndAddInt方法中几个参数的含义:
Object var1是传入操作对象,即AtomicInteger这个类的对象
long var2AtomicInteger对象的内存地址偏移量
int var4是本次执行的增量
int var5是从主内存中读取的期望值,用于写会主内存时比较使用

执行过程:
首先从主内存中获取期望值,然后执行CAS过程。如果执行失败,则继续从主内存中获取期望值,在执行CAS过程,直到CAS执行成功,返回结果。

CAS的缺点:

  1. 循环时间长,开销大。如果CAS失败,会一直进行尝试,如果CAS长时间一直不成功,可能会给CPU带来很大的开销。
  2. 只能保证一个共享变量的原子操作。对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就需要用锁来保证原子性。
  3. 会引入ABA问题

原子引用:

原子引用类指的是AtomicReference<V>,只需要指定一个类,该类即可称为原子类。

	/**
     * Creates a new AtomicReference with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicReference(V initialValue) {
        value = initialValue;
    }
    /**
     * Creates a new AtomicReference with null initial value.
     */
    public AtomicReference() {
    }
    /**
     * Sets to the given value.
     *
     * @param newValue the new value
     */
    public final void set(V newValue) {
        value = newValue;
    }

使用构造方法或者set(V newValue),可以使实例化的类具有原子特性。

CAS的ABA问题:

原子类AtomicInteger的ABA问题:
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差会导致数据的变化。

比如说一个线程1从内存中取出变量的值是A,这时另一个线程2也从内存中取出这个变量的值是A,并且线程2进行了一些操作将这个变量的值变成了B,然后线程2又将这个变量的值修改为A,这时候线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。
尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。

CAS的ABA问题解决:

使用AtomicStampedReference类解决
其主要思路是添加一个版本号来控制执行CAS变量的值是否修改过。

AtomicStampedReference源码:

	/**
     * Creates a new {@code AtomicStampedReference} with the given
     * initial values.
     *
     * @param initialRef the initial reference
     * @param initialStamp the initial stamp
     */
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }

initialRef为初始化值,initialStamp为初始化版本号。

获取当前版本号:

	/**
     * Returns the current value of the stamp.
     *
     * @return the current value of the stamp
     */
    public int getStamp() {
        return pair.stamp;
    }

获取当前变量的值:

	/**
     * Returns the current value of the reference.
     *
     * @return the current value of the reference
     */
    public V getReference() {
        return pair.reference;
    }

执行CAS的源码:

	/**
     * Atomically sets the value of both the reference and stamp
     * to the given update values if the
     * current reference is {@code ==} to the expected reference
     * and the current stamp is equal to the expected stamp.
     *
     * @param expectedReference the expected value of the reference
     * @param newReference the new value for the reference
     * @param expectedStamp the expected value of the stamp
     * @param newStamp the new value for the stamp
     * @return {@code true} if successful
     */
    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)));
    }

该方法有四个参数:
expectedReference期望的变量值
newReference要修改的变量值
expectedStamp期望的版本号
newStamp要修改的版本号

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java CAS(Compare And Swap,比较并交换)是一种常用于多线程编程的原子操作。其主要作用是在多线程环境下保证变量的原子性和一致性,解决多线程竞争条件下的并发问题。 Java CAS原理是通过比较内存中的值与期望值是否相等来确定是否进行交换,其核心思想是利用硬件的原子性操作来实现并发的同步,而不需要使用锁(synchronized)等机制。 具体来说,CAS操作包含三个参数:内存地址、旧的预期值和新的值。 1. 首先,CAS会将当前内存地址中的值与旧的预期值进行比较,如果相等,则说明内存中的值未被其他线程修改。 2. 然后,CAS会使用新的值来更新内存地址中的值,完成交换操作。 3. 最后,CAS会返回旧的预期值,可以通过返回值进行判断操作是否成功。 需要注意的是,CAS是一种乐观的并发控制方式,它不会阻塞线程,而是通过不断重试的方式来保证操作的原子性。如果CAS操作失败,那么线程会重新读取内存中的值,并重新尝试进行CAS操作,直到成功为止。 然而,CAS也存在一些问题。首先,CAS需要频繁地读取和写入内存,这对内存带宽的要求较高;其次,由于CAS操作是无锁的,因此存在ABA问题,即在操作过程中,如果其他线程修改了预期值两次并恢复为原来的值,CAS操作无法察觉。 总之,Java CAS作为一种基于硬件支持的原子操作,可以在多线程环境下实现高效的同步控制,然而它也需要开发人员自行处理ABA问题以及确保程序的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值