• CAS是什么


    CAS是什么?

    比较并交换

    例子1:

    public class ABADemo1 {
    public static void main(String[] args) {
    AtomicInteger atomicInteger = new AtomicInteger(5);
    System.out.println(atomicInteger.compareAndSet(5,2019)+" 当前值是:"+atomicInteger.get());
    System.out.println(atomicInteger.compareAndSet(5,1024)+" 当前值是:"+atomicInteger.get());
    }
    }

    结果如下:


    CAS底层原理?

    1.自旋锁

    2.UnSafe类

    atomicInteger.getAndIncrement();

    底层调用的其实是unsafe方法,关系见下图:

    va1 AtomicInteger对象本身。

    var2 该对象的引用地址。

    var4 需要变动的数量。

    var5  是用var1 var2找出的主内存中真实的值,用该对象当前的值与var5比较,如果相同,更新var5+var4并且返回true,如果不同,继续取值然后再比较,直到更新完成。(自旋)

     最后通过native调用本地方法,该方法的实现位于unsafe.cpp中。

     1.Unsafe类:

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

    注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务

    2.变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址来获取数据的。

    public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    3.变量vaue用volatile修饰,保证了多线程之间的内存可见性。

    CAS 的全称为Compare-And-Swap,它是一条CPU并发原语。

    它的功能是判断内存某个位置的值是否为预期值,这个过程是原子的

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


    CAS 的缺点

    1.循环时间长开销很大。

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

    3.会有ABA问题。

    CAS会导致"ABA问题"

    CAS算法实现一个重要前提需要取出内存中某时刻的数据并再当下时刻比较并替换,那么再这个时间差会导致数据的变化。

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


    AtomicReference原子引用例子:

    @Getter
    @ToString
    @AllArgsConstructor
    class User {
    String userName;
    int age;
    }

    /**
    * AtomicReference原子引用,想对某个类进行包装,可以参考这个类的写法
    */
    public class AtomicReferenceDemo {

    public static void main(String[] args) {
    User z3 = new User("z3",22);
    User li4 = new User("li4",25);
    AtomicReference<User> atomicReference = new AtomicReference<>();
    atomicReference.set(z3);
    System.out.println(atomicReference.compareAndSet(z3,li4)+" "+atomicReference.get().toString());
    System.out.println(atomicReference.compareAndSet(z3,li4)+" "+atomicReference.get().toString());
    }
    }

    运行结果见下图:


    解决ABA问题 加版本号例子:

    public class ABADemo {
    static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
    static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
    System.out.println("-------------存在ABA问题");
    new Thread(() ->{
    atomicReference.compareAndSet(100,101);
    atomicReference.compareAndSet(101,100);
    },"T1").start();

    new Thread(() ->{
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    System.out.println(atomicReference.compareAndSet(100,2019) +" " +atomicReference.get());
    },"t2").start();

    try {
    TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }

    System.out.println("-------------以下是解决ABA问题");

    new Thread(() ->{
    int stamp = atomicStampedReference.getStamp();
    System.out.println(Thread.currentThread().getName()+" 第1次版本号"+stamp);
    try {
    TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
    System.out.println(Thread.currentThread().getName()+" 第2次版本号"+atomicStampedReference.getStamp());
    atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
    System.out.println(Thread.currentThread().getName()+" 第3次版本号"+atomicStampedReference.getStamp());
    },"t3").start();


    new Thread(() ->{
    int stamp = atomicStampedReference.getStamp();
    System.out.println(Thread.currentThread().getName()+" 第1次版本号"+stamp);
    try {
    TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    boolean result = atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);
    System.out.println(Thread.currentThread().getName()+" 修改成功否"+result+" 当前版本号"+atomicStampedReference.getStamp());

    System.out.println(Thread.currentThread().getName()+" 当前实际最新值"+atomicStampedReference.getReference());
    },"t4").start();
    }
    }
    运行结果见下图:

  • 相关阅读:
    Android Shape画圆,矩形
    Android 图片平铺效果实现的3种方法
    threadid=1: thread exiting with uncaught exception (group=0x40db8930)
    Facebook 调试工具Stetho配置入门
    Exception in MessageQueue callback: handleReceiveCallback
    EditText 双击才能获取点击事件
    2011年中国(大陆)地级以上(含省直辖县)行政区划表
    Android 应用接入广点通统计API 方案
    Android常用工具类封装---SharedPreferencesUtil
    IIS上发布站点后URL重写失效的解决方法
  • 原文地址:https://www.cnblogs.com/liuyi13535496566/p/12142820.html
Copyright © 2020-2023  润新知