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

Posted gocode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子更新引用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并发编程的艺术》方腾飞

以上是关于原子更新引用AtomicReference实现原理分析的主要内容,如果未能解决你的问题,请参考以下文章

(转)Java 原子性引用 AtomicReference

Java并发多线程编程——原子类AtomicInteger的ABA问题及原子更新引用

原子类与自旋锁原理初探

原子类与自旋锁原理初探

Java并发AtomicReference类

Java多线程系列---“JUC原子类”05之 AtomicReference原子类