原子更新引用AtomicReference实现原理分析
原子更新基本类型只能更新单个变量,而原子更新引用类型可以原子更新多个变量。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被所有立即可见)
compareAndSet
和weakCompareAndSet
均是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\n", flag, idAtomicRef); id1.setCreated(new Date(System.currentTimeMillis() +3600*1000)); flag= idAtomicRef.compareAndSet(id2, id1); System.out.printf("CAS successful? %s,current ID:%s\n", 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并发编程的艺术》方腾飞