美文网首页
【并发包】Atomic相关

【并发包】Atomic相关

作者: 良辰夜 | 来源:发表于2019-05-03 11:17 被阅读0次

    1. 统一API一览

    注意:

    1. oldValue指方法内部在执行操作前,通过get方法获取得值,而newValue则是已经执行操作后的值
    2. 一般get在前面,说明返回的是旧值否则为新值。
    方法 参数 返回 详细解释
    set newValue void validate式设置
    lazySet newValue void 赋值式设置(其他线程不一定可见)
    getAndSet newValue oldValue validate式设置
    compareAndSet expect,update boolean 比较expect,如相同则设置并返回true,如不同则不设置并返回false
    weakCompareAndSet expect,update boolean
    getAndIncrement void oldValue 原子++
    getAndDecrement void oldValue 原子--
    getAndAdd delta oldValue 原子+delta
    incrementAndGet void newValue 原子++
    decrementAndGet void newValue 原子--
    addAndGet delta newValue 原子+delta
    getAndUpdate xxxUnaryOperator (FunctionalInterface ,一个参数一个返回结果) oldValue 把oldValue给xxxUnaryOperator
    updateAndGet xxxUnaryOperator (FunctionalInterface ,一个参数一个返回结果) newValue 把oldValue给xxxUnaryOperator得到返回值并返回
    getAndAccumulate x,xxxBinaryOperator oldValue 把x和oldValue给xxxBinaryOperator
    accumulateAndGet x,xxxBinaryOperator newValue 把x和oldValue给xxxBinaryOperator得到返回值并返回

    2 AtomicInt (细讲)

    2.1一些变量

    unsafe :每个atomic类基本都必须使用这个类。
    valueOffset:value这个变量在atomicInt这个类的内存上的偏移值。
    value:当前对象的值。

    private static final Unsafe unsafe = Unsafe.getUnsafe();
        private static final long valueOffset;
        static {
            try {
                //通过字节码,拿到value字段相较于整个atomic对象的偏移值。
                valueOffset = unsafe.objectFieldOffset
                    (AtomicInteger.class.getDeclaredField("value"));
            } catch (Exception ex) { throw new Error(ex); }
        }
        private volatile int value;
    

    2.2 set/lazySet

    //更新值时,因为value是volatile,所以其他线程立刻可见。
    public final void set(int newValue) {
        value = newValue;
    }
    
    // 更新值时,不保证其他线程可见。相对于set应该有性能上优势。
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }
    

    2.3 getAndSet

    public final int getAndSet(int newValue) {//返回上一次的值,并set新的value
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
    
    public final int getAndSetInt(Object o, long offset, int newValue) {
        int v;
        do {
            v = getIntVolatile(o, offset);//通过偏移量拿到value
        } while (!compareAndSwapInt(o, offset, v, newValue));//如果这次value和上次value不同,那么返回false
        return v;
    }
    

    2.4 compareAndSet、weakCompareAndSet

    在java9之前,这个两个方法基本一样,我们尽量用compareAndSet。

    //比较expect是否与value值一直,如果不一致,就返回false,并不更新。
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    

    2.5 unsafe.getAndAddInt 相关的方法

    先看 getAndAddInt 方法源码

    //可以看出这个方法,就是原子性对value加上一个delta,并返回原值
    public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {
            v = getIntVolatile(o, offset);//通过偏移量拿到值
        } while (!compareAndSwapInt(o, offset, v, v + delta));//原子性对值+delta。
        return v;
    }
    

    在看atomic相关方法:

    //对value+1并返回原值
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    //对value-1并返回原值
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
    //对value加上一个delta并返回原值
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    //对value+1并返回新值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
    //对value-1并返回新值
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }
    //对value加上一个delta并返回新值
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }
    

    2.6 getAndUpdate、updateAndGet、getAndAccumulate、accumulateAndGet

    //将旧值传给updateFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回旧值
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    //将旧值传给updateFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回新值
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
    //将旧值和x传给accumulatorFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回旧值
    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    //将旧值和x传给accumulatorFunction得到新值,然后比较更新,如失败则重复循环,直至成功返回新值
    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }
    

    2.7 总结 AtomicInt

    1.如果前面是getxxx,那么更新后返回旧值,否则就返回更新后的新值。
    2.核心方法,都是依靠compareAndSet

    3. AtomicBoolean

    value是通过int来实现,几乎和AtomicInteger一模一样,其中0表示false,而1表示true。

    private volatile int value;
    public AtomicBoolean(boolean initialValue) {
        value = initialValue ? 1 : 0;
    }
    

    4. AtomicLong

    由于AtomicLong方法基本和前面AtomicInt一致所以不细写Api分析了。
    仅仅写下唯一差别。

    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();
    private static native boolean VMSupportsCS8();
    

    这些事给jvm来调用的。
    有些cpu是32位的数据总线一次就只能传32个字节,long是64个字节,可能需要传两次,这样就不能保持原子性。所以需要加锁。

    如果JVM的long操作是原子化的,会采用无锁的CAS来更新,如果不支持就会使用带锁的方式来更新。

    所以尽量使用int类型。如果一定要用long类型,尽量保证cpu是64位的,系统是64位的。

    5. AtomicReference

    和前面的不同,AtomicReference 提供了对Obeject类型的compareAndSet,当然compare的是java引用

    API和前面的atomicInteger基本一模一样(当然原子++--都没有了)。

    6. ABA问题和AtomicStampedReference

    6.1 ABA问题引入。

    线程要修改变量值:
    线程t1: a->c
    线程t2: a->b->a

    t1先拿到变量值为a,然后t2执行a->b->a,此时线程t1 进行CAS发现变量值还是a,所以将a->c。
    对比sychronized,如果使用sychronized,线程t2就无法对变量进行修改。
    而使用CAS则会导致,线程t1仅仅只能比较变量值是否相等,而无法知道之前是否已经发生变化了。

    这其实和挪用资产挺像的,比如小张挪用100W公司资产炒股,赚了10W,让后把100W补回公司,这样公司查账后发现资产没变化,但是小张多了10W

    6.2 AtomicStampedReference

    可以看出来CAS其实就好像乐观锁一样,因此我们可以参考一下SVN的解决方案加上version。

    而AtomicStampedReference中的Stamped就是类似version的做法。

    6.2.1 Pair

    Pair可以说是唯一个和AtomicReference 类的区别。
    Pair的作用就是给value,也就是给 T refresh绑定了一个时间搓。返回一个新类叫pair。

    private static class Pair<T> {
        final T reference;
        final int stamp;
        private Pair(T reference, int stamp) {
            this.reference = reference;
            this.stamp = stamp;
        }
        static <T> Pair<T> of(T reference, int stamp) {
            return new Pair<T>(reference, stamp);
        }
    }
    private volatile Pair<V> pair;
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }
    
    6.2.2 compareAndSet

    就写法而言还是有点炫酷,类似js的写法,如果是A&&B那么A为true会执行B,如果是A||B,那么A为false则会执行B。

    1. 这里先比较传进来的expectedReference和expectedStamp是否和当前pair一致,如果不一致返回false。
    2. 再比较传进来的newReference和newStamp是否和pair一一致,如果一一致则直接返回true。
    3. 如果不一致,则进行CAS后返回。
    public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp) {
        Pair<V> current = pair;
        return
            expectedReference == current.reference && expectedStamp == current.stamp //1  
            && (
                    (newReference == current.reference && newStamp == current.stamp) //2
                    || casPair(current, Pair.of(newReference, newStamp))//3
            );
    }
    

    7.AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

    作用:对数组内部的元素进行原子操作。

    我们以AtomicIntegerArray为例:

    7.1 int数组内存中结构

    可以看出主要由两部分构成:header ,int 元素
    其中每个int元素为4个字节,而header则是固定的。


    int 数组在内存中的结构

    7.2 分析AtomicIntegerArray核心代码

    private static final int base = unsafe.arrayBaseOffset(int[].class);
    private static final int shift;
    private final int[] array;
    
    static {
        int scale = unsafe.arrayIndexScale(int[].class);//拿到每个元素占用的字节
        if ((scale & (scale - 1)) != 0)//判断是否为2的幕
            throw new Error("data type scale not a power of two");
        shift = 31 - Integer.numberOfLeadingZeros(scale);//相当于Integer.numberOfTrailingZeros。
    }
    
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);
    
        return byteOffset(i);
    }
    
    private static long byteOffset(int i) {
        return ((long) i << shift) + base;//i<<shift是指 i前面占用的offset
    }
    

    相关文章

      网友评论

          本文标题:【并发包】Atomic相关

          本文链接:https://www.haomeiwen.com/subject/xklgnqtx.html