• 原子更新引用AtomicReference实现原理分析


    1 前言

    原子更新基本类型只能更新单个变量,而原子更新引用类型可以原子更新多个变量。Atomic包提供了以下3个类。

    • AtomicReference:原子更新引用类型。

    • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

    • AtomicMarkableReference:原子更新带有标记位的引用类型(可以原子更新一个布尔类型的标记位和引用类型。)

    现在先来看看AtomicReference是如何实现的(基于JDK1.8)

    2 实现过程分析

    主要字段

    private static final Unsafe unsafe = Unsafe.getUnsafe();//Unsafe所有并发工具的基础工具类
    private static final long valueOffset; //成员变量value在当前AtomicReference对象中的地址偏移量
    static {
        try {//使用objectFieldOffset获取偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicReference.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    ​
    private volatile V value;//value表示我们要修改/更新的目标对象

    AtomicReference内部有一个泛型成员变量value,此变量表示我们要修改/更新的目标对象,此属性是AtomicReference的核心。它被

    volatile关键字修饰,使用Unsafe工具类能实时地修改/读取value的引用。

     

    value 属性可以通过构造方法AtomicReference(V initialValue)初始化指定,还也可在利用set(V newValue)方法重新设定。

    public AtomicReference(V initialValue) {
        value = initialValue;
    }
    public AtomicReference() {
    }
    public final void set(V newValue) {
        value = newValue;
    }

    get方法:返回当前最新的值

    lazySet方法:最终设定指定值 (延迟化设值,不保证新值newValue被所有立即可见)

    compareAndSetweakCompareAndSet均是CAS更新值(引用)。

    getAndSet: 原子地设置新值并返回旧值(只有更新成功后方法才能返回)

        public final V get() {
            return value;
        }
        //最终设定 (延迟化设值,不保证新值newValue被所有立即可见)
        public final void lazySet(V newValue) {
            unsafe.putOrderedObject(this, valueOffset, newValue);
        }
        // CAS更新,(若expect==value,就将value设为update)并返回是否更新成功的布尔值
        public final boolean compareAndSet(V expect, V update) {
            return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
        }
        //同上的CAS,(方法定义上是对引用做弱比较,但实现细节和上面的compareAndSet相同)
        public final boolean weakCompareAndSet(V expect, V update) {
            return unsafe.compareAndSwapObject(this, valueOffset, expect, update);
        }
    ​
        //设置新值并返回旧值
        public final V getAndSet(V newValue) {
            return (V)unsafe.getAndSetObject(this, valueOffset, newValue);
        }
    ​
        //Unsafe的getAndSetObject
        public final Object getAndSetObject(Object o, long offset, Object newValue) {
            Object v;
            do {
                v = getObjectVolatile(o, offset); //获取当前的值
            } while (!compareAndSwapObject(o, offset, v, newValue));//CAS更新
            return v;
        }

    getAndUpdate 将单参数(实参为内部value)函数接口的返回值设为新值并返回旧值

    updateAndGet 将单参数(实参为内部value)函数接口的返回值设为新值并返回新值

    getAndAccumulate :将双参数(实参之一是内部value,另一个是显式指定的x)函数接口返回值作为新值并返回旧值

    accumulateAndGet:将双参数(实参之一是内部value,另一个是x)函数接口返回值作为新值并返回新值

    public final V getAndUpdate(UnaryOperator<V> updateFunction) {
        V prev, next;
        do {
            prev = get();//获取当前的value
            next = updateFunction.apply(prev); //updateFunction根据输入参数prev得到value新值
        } while (!compareAndSet(prev, next));//CAS更新
        return prev;
    }
    public final V updateAndGet(UnaryOperator<V> updateFunction) {
        V prev, next;
        do {
            prev = get();
            next = updateFunction.apply(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
    public final V getAndAccumulate(V x,
                                    BinaryOperator<V> accumulatorFunction) {
        V prev, next;
        do {
            prev = get();//获取当前的value
            next = accumulatorFunction.apply(prev, x);//accumulatorFunction根据输入参数prev、x两者得到value新值
        } while (!compareAndSet(prev, next));//CAS更新
        return prev;
    }
    public final V accumulateAndGet(V x,
                                    BinaryOperator<V> accumulatorFunction) {
        V prev, next;
        do {
            prev = get();
            next = accumulatorFunction.apply(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }

     

    3 使用示例

    public class AtomicRef {
        static class ID {
            private long serialNum;
            private String algo;
            private Date created;
            public ID(long serialNum, String algo, Date created) {
                this.serialNum = serialNum;
                this.algo = algo;
                this.created = created;
            }
      //getter/setter省略
        }
        public static void main(String[] args) {
            AtomicReference<ID> idAtomicRef = new AtomicReference<>();
    ​
            ID id1 = new ID(1248L, "random", new Date());
            ID id2 = new ID(3456L, "random", new Date());
    ​
            idAtomicRef.set(id1);
            Boolean flag = idAtomicRef.compareAndSet(id1, id2);
            System.out.printf("CAS successful? %s,current ID:%s
    ", flag, idAtomicRef);
    ​
            id1.setCreated(new Date(System.currentTimeMillis() +3600*1000));
            flag= idAtomicRef.compareAndSet(id2, id1);
            System.out.printf("CAS successful? %s,current ID:%s
    ", flag, idAtomicRef);
    ​
            System.out.println(idAtomicRef);
    ​
            ID id3 = new ID(1024L, "Random", new Date());
            idAtomicRef.set(id3);
    ​
            ID id4 = idAtomicRef.updateAndGet(id -> new ID(id.getSerialNum() * 10, "scale", new Date()));
    ​
            ID id5 = idAtomicRef.accumulateAndGet(id1, (x, y) -> {
                long serialNum = x.serialNum + y.serialNum;
                long time = (x.getCreated().getTime() + y.getCreated().getTime()) / 2;
                return new ID(serialNum, "accumulate", new Date(time));
            });
            System.out.println(id4);
            System.out.println(id5);
        }
    }

     

    参考: 《 Java并发编程的艺术》方腾飞

  • 相关阅读:
    Servlet的生命周期?
    C++图结构的图结构操作示例
    如何从google play下载app应用,直接下载apk
    C# Socket异步聊天例子
    三极管饱和,放大,截止电压判断
    java中的浮点(float)运算
    微软2014校园招聘笔试试题
    软件开发中的资源控制问题学习
    linux mount命令学习
    17、Spring Boot普通类调用bean【从零开始学Spring Boot】
  • 原文地址:https://www.cnblogs.com/gocode/p/analysis-source-code-of-AtomicReference.html
Copyright © 2020-2023  润新知