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被所有线程立即可见)
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并发编程的艺术》方腾飞