CAS你知道吗?

CAS 是什么???

1、比较并交换(CompareAndSet)通过简单的Demo来看

package com.brian.interview.study.thread;

/**
 * Copyright (c) 2020 ZJU All Rights Reserved
 * <p>
 * Project: JavaSomeDemo
 * Package: com.brian.interview.study.thread
 * Version: 1.0
 * <p>
 * Created by Brian on 2020/2/11 0:47
 */

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 1、  CAS是什么? ===> CompareAndSet
 *         比较并交换
 */
public class CASDemo {
    public static void main(String[] args) {
        AtomicInteger atomicInteger = new AtomicInteger(5);

        // main do thing ......

        System.out.println(atomicInteger.compareAndSet(5, 2020)+"\t current data: "+atomicInteger.get());  // atomicInteger 本来就是5,修改成功

        System.out.println(atomicInteger.compareAndSet(5, 1024)+"\t current data: "+atomicInteger.get());  // atomicInteger 现在是2020,不是5,修改失败
    }
}

2、CAS 底层原理?同时谈谈 Unsafe 的理解

atomicInteger.getAndIncrement() 方法的源代码:

在这里插入图片描述
引出来一个问题:Unsafe 类什么?

Unsafe 类:

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

2、变量 valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址获取数据的。
在这里插入图片描述
3、变量 value 用 volatile 修饰,保证了多线程之间的内存可见性。

CAS 是什么:

unsafe.getAndAddInt

var1 AtomicInteger 对象本身
var2 该对象值得引用地址
var4 需要变动的数量
var5 是用过 var1 var2 找出的主内存中真实的值
用该对象当前的值与 var5 比较:
如果相同,更新 var5+var4 并且返回 true,
如果不同,继续取值然后再比较,直到更新完成。
在这里插入图片描述
假设线程A和线程B两个线程同时执行 getAndAddInt 操作(分别跑在不同CPU上):
1、AtomicInteger 里面的 value 原始值为 3,即主内存中 AtomicInteger 的 value 为 3,根据 JMM 模型,线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存。

2、线程A通过 getIntVolatile(var1, var2) 拿到 value 值 3,这时线程A被挂起。

3、线程B也通过 getIntVolatile(var1, var2) 方法获取到 value 值3,此时刚好线程B没有被挂起并执行 compareAndSwapInt 方法比较内存值也为3,成功修改内存值为4,线程B打完收工,一切OK。

4、这时线程A恢复,执行 compareAndSwapInt 方法比较,发现自己手里的值数字3和主内存的值数字4不一致,说明该值已经被其它线程抢先一步修改过了,那A线程本次修改失败,只能重新读取重新来一遍了

5、线程A重新获取 value 值,因为变量 value 被 volatile 修饰,所以其它线程对它的修改,线程A总是能够看到,线程A继续执行 compareAndSwapInt 进行比较替换,直到成功。

底层汇编

Unsafe类中的 compareAndSwapInt,是一个本地方法,该方法的实现位于 unsafe.cpp 中

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
    UnsafeWrapper("Unsafe_CompareAndSwapInt");
    cop p = JNIHandles::resolve(obj);
    jint* addr = (jint*)index_oop_from_field_offset_long(p, offset);
    return (jint)(Atomic::cmpxchg(x, addr, e))==e;
UNSAFE_END
// 先想办法拿到变量 value 在内存中的地址。
// 通过 Atomic::cmpxchg 实现比较替换,其中参数x是即将更新的值,参数e是原内存的值。
简单版小总结

CAS (CompareAndSwap)
比较当前工作内存中的值和主内存中的值,如果相同则执行规定操作,
否则继续比较直到主内存和工作内存中的值一致为止。

CAS应用
CAS有3个操作数,内存值V,旧的预期值A,要修改的更新值B。
当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

3、CAS 缺点

循环时间长开销很大

我们可以看到 getAndAddInt 方法执行时,有个 do while
在这里插入图片描述
如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

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

当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,

但是

对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁来保证原子性。

发布了74 篇原创文章 · 获赞 75 · 访问量 4036

猜你喜欢

转载自blog.csdn.net/qq_35340189/article/details/104563983