zoukankan      html  css  js  c++  java
  • CAS原理

    简介

    CAS(Compare and swap)比较和替换是设计并发算法时用到的一种技术。简单来说,比较和替换是使用一个期望值和一个变量的当前值进行比较,如果当前变量的值与我们期望的值相等,就使用一个新值替换当前变量的值。

            AtomicInteger atomicInteger = new AtomicInteger(10);
            System.out.println(atomicInteger.compareAndSet(10, 4));
            System.out.println(atomicInteger.compareAndSet(10, 2));
            System.out.println(atomicInteger);
    

    底层原理

    以AtomicInteger的getAndIncrement方法为例:

        public final int getAndIncrement() {
            //this:当前对象
            //valueOffset:内存地址偏移量(内存地址)
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }
    

    这里由unsafe调用getAndAddInt方法。

    Unsafe

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

    Unsafe类中的方法都可以直接调用操作系统底层资源执行相应任务。

    image-20210121153356036

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

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

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

    原语的执行必须是连续的,执行过程中不允许打断,所以CAS是一条CPU的原子指令,所以不会造成所谓的数据不一致问题。

    image-20210121154448868

    var1:AtomicInteger对象本身

    var2:该对象值的引用地址

    var4:需要变动的值

    var5:是用过var1 var2找出的主内存中真实的值

    compareAndSwapInt:var2和var5比较,相同则 var5+var4,并返回true,跳出do-while循环。如果不同,继续取值再比较,直至更新完成。

    CAS缺点

    1)如果CAS失败,会一直尝试,如果CAS长时间不成功,可能会给CPU带来很大的开销

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

    3)ABA问题

    ABA

    如果一个线程1从内存位置V中取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作将值编程了B,然后线程2又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程1操作成功。

    尽管线程1的CAS操作成功,但是不代表这个过程就是没有问题的。

    原子引用

    AtomicReference:原子引用

     //toString,Getter,Setter省略 
    class Stu{
        private String name;
    
        public Stu(String name) {
            this.name = name;
        }
    
    }
    
    public class AtomicReferenceDemo {
        public static void main(String[] args) {
            Stu zs = new Stu("zs");
            Stu ls = new Stu("ls");
    
            AtomicReference<Stu> reference = new AtomicReference<>(zs);
    
            System.out.println(reference.compareAndSet(zs,ls));
            System.out.println(reference.compareAndSet(zs,ls));
        }
    }
    

    ABA问题解决

    新增一种机制,修改版本号(类似时间戳)

    存在ABA问题的代码:

            new Thread(()->{
                atomicReference.compareAndSet(10,11);
                atomicReference.compareAndSet(11,10);
            },"t1").start();
    
            new Thread(()->{
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(atomicReference.compareAndSet(10,11));
            },"t2").start();
    

    使用AtomicStampedReference解决:

        private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(10,1);
    
        public static void main(String[] args) {
            new Thread(()->{
                int stamp = atomicStampedReference.getStamp();
                System.out.println("t3 first stamp :"+stamp);
                atomicStampedReference.compareAndSet(10,11,1,2);
                atomicStampedReference.compareAndSet(11,10,2,3);
            },"t3").start();
    
            new Thread(()->{
                try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println(atomicStampedReference.compareAndSet(10, 11, 1, 2));
            },"t4").start();
        }
    
  • 相关阅读:
    第1章 数据结构绪论
    收集的名言警句
    Asp.net MVC知识积累
    我的书单
    ASP.NET Web API
    贱人语录
    正则表达式入门
    Lucene 3.0
    Solr之java操作
    Elasticsearch
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/14309053.html
Copyright © 2011-2022 走看看