zoukankan      html  css  js  c++  java
  • Atomic实现原子性源码分析:CAS(比较并交换)、Unsafe类

    1.CAS:
    比较并交换(Compare And Swap),是Unsafe类中的一条CPU系统原语,原语的执行必须是连续的,在执行过程中不允许被中断,即CAS是一条CPU的原子指令,不会造成数据不一致问题。
    类似:Atomic中的compareAndSet()方法
    如果期望值和真实值相同,本次操作成功,如果不同则失败,这个过程是原子的
    AtomicInteger atomicInteger = new AtomicInteger(5);
    ...
    atomicInteger.compareAndSet(5, 2019); // 期望是5的时候,将其赋值为2019

    2.Unsafe类:

    rt.jar中的原生类,也是CAS的核心类,Unsafe类都是native修饰的,Unsafe类的方法都直接操作内存
    以getAndIncrement()为例
    atomicInteger.getAndIncrement();    // i++

    AtomicInteger源码:

    1)getAndIncrement()源码:

    /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    valueOffset为变量值在内存中的偏移地址

    2)AtomicInteger的value和valueOffset成员

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    
    private volatile int value;

    unsafe类源码:

    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;
    }

    var1为当前AtomicInteger对象,var2是该对象值的引用地址,var4是需要增加的数量

    其过程为:
    1)首先在物理内存中获取当前对象var1在var2地址上的值var5;
    2)比较该地址当前最新的值和我获取到的值var5是否相等,如果相等,则赋值var5+var4;
    3)否则重新获取重复1、2。
    3.场景举例
    假设线程A和线程B同时执行getAndAddInt操作(分别跑在不同CPU上)
    1)AtomicInteger中value原始值为3,即主内存中为3,根据JMM模型,线程A和线程B各自持有一份value为3的副本分别在各自的工作内存中。
    2)线程A通过getIntVolatile(var1, var2)拿到value的值3,这时线程A被挂起。
    3)线程B也通过getIntVolatile(var1, var2)方法获取到value值3,刚好B没有被挂起并执行compareAdeSwapInt方法,比较内存值也为3,成功修改内存值为4,线程B执行完毕。
    4)这时线程A恢复,执行compareAndSwapInt方法比较,发现自己工作内存中的value3和主内存中的4不一致,说明该值已经被其他线程抢先一步修改过了,线程A本次修改失败,只能重新读取重新来一遍了。
    5)线程A重新获取value值,因为变量value被volatile修饰,所以其他线程对它的修改,线程A总是能够看到,线程A继续执行compareAndSwapInt进行比较替换,直到成功。
    4.缺点:
    1)CAS的方式相比于锁来说并发性加强了,但如果CAS失败,会一直进行尝试,可能会给CPU带来很大的开销
    2)只能保证一个共享变量的原子性,对多个共享变量操作可以用锁来保证原子性。
    3)引出ABA问题:不同线程执行时间不均衡导致慢的线程获取到虽然值与期望相等,却中间经历了别的线程更新操作。
  • 相关阅读:
    fedora 20 install skype
    WebsitePanel(wsp)配置详解(安装指南)
    【转】SQL Server 2008下载 (附注册码)
    SQL SERVER树型数据处理时,函数递归调用问题,查询根节点,子节点函数
    150 Opening ASCII mode data connection. FTP连接的PASV和PORT方式
    如何在IIS7上配置 FTP7并使用IIS管理凭据方式进行验证?
    DZ!NT论坛 3.6.711删除用户各种错解决方案
    在虚拟机上安装红帽Linux.5.5.for.x86.服务器版系统(按针对安装oracle 10g作的配置)
    OpenStack Nova 制作 Windows 镜像
    HttpWebRequest访问时,错误:(401)未经授权。
  • 原文地址:https://www.cnblogs.com/MWCloud/p/11460186.html
Copyright © 2011-2022 走看看