zoukankan      html  css  js  c++  java
  • Java 中 CAS

    一、CAS 概念

    CAS ,全称 Compare And Swap(比较与交换),解决多线程并行情况下使用锁造成性能损耗的一种机制。

    实现思想 CAS(V、A、B) ,V为内存地址,A为预期原值,B 为新值。如果内存地址的值与预期原址相匹配,那么将该位置值更新为新值。否则说明已经被其他线程更新,处理器不做任何处理。

    无论哪种情况,它都会在 CAS 指令之前返回该位置的值。而我们可以使用自旋锁,循环CAS ,重新读取该变量再次尝试修改该变量,也可以放弃操作

     CAS操作由处理器提供支持,是一种原语。原语是操作系统或计算机网络用语范畴。是由若干条指令组成的,用于完成一定功能的一个过程,具有不可分割性,即原语的执行必须是连续的,在执行过程中不允许被中断。如 Intel 处理器,比较并交换通过指令的 cmpxchg 系列实现。处理器相关指令不做过多介绍,有兴趣的可自行查阅资料。

    二、JDK1.8 中的CAS

    Unsafe类,在sun.misc包下,不属于Java标准。Unsafe类提供一系列增加Java语言能力的操作,如内存管理、操作类/对象/变量、多线程同步等。其中与CAS相关的方法有以下几个:

    //var1为CAS操作的对象,offset为var1某个属性的地址偏移值,expected为期望值,var2为要设置的值,利用JNI来完成CPU指令的操作
    public final native boolean compareAndSwapObject(Object var1, long offset, Object expected, Object var2);
    public final native boolean compareAndSwapInt(Object var1, long offset, int expected, int var2);
    public final native boolean compareAndSwapLong(Object var1, long offset, long expected, long var2);
    /** 如果CAS成功,return oldValue, oldValue =  oldValue + addValue
         *  如果CAS失败,自旋,一直运行,直到成功为止
         */
        public final Xxx getAndAddXxx(Object var1, long offset, long addValue) {
            int oldValue;
            do {
                oldValue = this.getIntVolatile(var1, offset);
            } while(!this.compareAndSwapInt(var1, offset, oldValue, oldValue + addValue));
    
            return oldValue;
        }
    
        /** 如果CAS成功,return oldValue, oldValue =  newValue
         *  如果CAS失败,自旋,一直运行,直到成功为止
         */
        public final Xxx getAndSetXxx(Object var1, long offset, Object newValue) {
            int oldValue;
            do {
                oldValue = this.getXxxVolatile(var1, offset);
            } while(!this.compareAndSwapXxx(var1, offset, oldValue, newValue));
    
            return oldValue;
        }

     一般不建议使用Unsafe类,除非对它有很深入的了解。

      

      java.util.concurrent包中大量使用了CAS原理,如AtomicInteger类,都是调用上面几个Unsafe方法保证多线程数据的正确性

      以下是AtomicInteger的CAS操作相关源码

    public class AtomicInteger extends Number implements java.io.Serializable {
        private static final long serialVersionUID = 6214790243416807050L;
    
        // setup to use Unsafe.compareAndSwapInt for updates
        // Unsafe类,提供一系列增强Java的功能,如内存管理、操作类/对象/变量、多线程同步等。不建议开发者调用
        private static final Unsafe unsafe = Unsafe.getUnsafe();
        // 获取对象某个属性的地址偏移值
        private static final long valueOffset;
    
        static {
            try {
                // value相对“起始地址”的偏移量
                valueOffset = unsafe.objectFieldOffset
                        (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
    
        // value值, volatile修饰,保证不同线程间的可见性
        private volatile int value;
        public AtomicInteger(int initialValue) { value = initialValue; }
        public AtomicInteger() {}
    
        public final int get() { return value; }
        public final void set(int newValue) { value = newValue; }
    
        /**
         * Eventually sets to the given value.
         *
         * @param newValue the new value
         * @since 1.6
         */
        public final void lazySet(int newValue) {
            //有序或者有延迟的putIntVolatile方法
            unsafe.putOrderedInt(this, valueOffset, newValue);
        }
    
        /**
         * Atomically sets to the given value and returns the old value.
         * @param newValue the new value
         * @return the previous value
         */
        public final int getAndSet(int newValue) {
            return unsafe.getAndSetInt(this, valueOffset, newValue);
        }
    
        /**
         * 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(int expect, int update) {
            // JNI调用,实现CAS
            return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
        }
    
        /**
         * i++ 操作
         * Atomically increments by one the current value.
         * @return the previous value
         */
        public final int getAndIncrement() {
            return unsafe.getAndAddInt(this, valueOffset, 1);
        }
    
        /**
         * i-- 操作
         * Atomically decrements by one the current value.
         * @return the previous value
         */
        public final int getAndDecrement() {
            return unsafe.getAndAddInt(this, valueOffset, -1);
        }
    
        /**
         * return i, i = i + n 操作
         * Atomically adds the given value to the current value.
         * @param delta the value to add
         * @return the previous value
         */
        public final int getAndAdd(int delta) {
            return unsafe.getAndAddInt(this, valueOffset, delta);
        }
    
        /**
         * ++i 操作
         * Atomically increments by one the current value.
         * @return the updated value
         */
        public final int incrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
        }
    
        /**
         * --i 操作
         * Atomically decrements by one the current value.
         * @return the updated value
         */
        public final int decrementAndGet() {
            return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
        }
    
        /**
         * i = i + n ,return i操作
         * Atomically adds the given value to the current value.
         * @param delta the value to add
         * @return the updated value
         */
        public final int addAndGet(int delta) {
            return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
        }
        // 其余函数,略...

    三、CAS缺点

     CAS有几个缺点:

      1、ABA问题。当第一个线程执行CAS操作,尚未修改为新值之前,内存中的值已经被其他线程连续修改了两次,使得变量值经历 A -> B -> A的过程。

      解决方案:添加版本号作为标识,每次修改变量值时,对应增加版本号; 做CAS操作前需要校验版本号。JDK1.5之后,新增AtomicStampedReference类来处理这种情况。

      2、循环时间长开销大。如果有很多个线程并发,CAS自旋可能会长时间不成功,会增大CPU的执行开销。

      3、只能对一个变量进原子操作。JDK1.5之后,新增AtomicReference类来处理这种情况,可以将多个变量放到一个对象中。

    参考:https://www.cnblogs.com/Shuuichi/p/10590710.html

  • 相关阅读:
    2021 2月 构建之法读书笔记
    2021 2 7 体温登记App开发总结
    2021 2 3android开发学习笔记 8
    2021 2 2 android开发学习笔记 7
    2021 1月 梦断代码读后感
    2021 2 1 android开发学习笔记 6
    2021 1 31 Android开发 学习笔记 5
    2021 1 28 android开发学习笔记 4
    2021 1 27 android开发学习笔记3
    Java作业(一)
  • 原文地址:https://www.cnblogs.com/bytecodebuffer/p/11972870.html
Copyright © 2011-2022 走看看