1 上节回顾
我们在java.util.concurrent.atomic源码学习(一)https://www.jianshu.com/p/d865264818af中介绍了原子类,原子数组,以及带计数器的原子类,下面我们将介绍对于非原子变量,我们如何实现原子操作,主要是看AtomicXXXFieldUpdater的源代码,下面以AtomicReferenceFieldUpdater为例进行分析。
2 AtomicReferenceFieldUpdater源代码阅读
我们在上一节了解到想要进行对变量进行原子操作,unsafe方法入参需要类对象,以及修改field的内存偏移位置,类对象比较好传,难得的是如何获取待修改field的内存偏移位置,在AtomicReferenceFieldUpdater中是通过反射获取待修改field的内存偏移位置的,代码如下
/**
** 首先是构造器,构造器new了一个实现类,
** Reflection.getCallerClass()从名字上来看是获取调用类的信息,
** 主要用途是类安全,判断Caller类有没有修改filed的权限
**/
@CallerSensitive
public static <U,W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,
Class<W> vclass,
String fieldName) {
return new AtomicReferenceFieldUpdaterImpl<U,W>
(tclass, vclass, fieldName, Reflection.getCallerClass());
}
/**
** getAndUpdate方法都差不多,只是入参多了一个obj
** @param obj An object whose field to get and set
** 这个obj是持有需要更新的filed的原始对象
**/
public final V getAndUpdate(T obj, UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get(obj);
next = updateFunction.apply(prev);
} while (!compareAndSet(obj, prev, next));
return prev;
}
public final V updateAndGet(T obj, UnaryOperator<V> updateFunction) {
V prev, next;
do {
prev = get(obj);
next = updateFunction.apply(prev);
} while (!compareAndSet(obj, prev, next));
return next;
}
public final V getAndAccumulate(T obj, V x,
BinaryOperator<V> accumulatorFunction) {
V prev, next;
do {
prev = get(obj);
next = accumulatorFunction.apply(prev, x);
} while (!compareAndSet(obj, prev, next));
return prev;
}
public final V accumulateAndGet(T obj, V x,
BinaryOperator<V> accumulatorFunction) {
V prev, next;
do {
prev = get(obj);
next = accumulatorFunction.apply(prev, x);
} while (!compareAndSet(obj, prev, next));
return next;
}
下面我们揭开AtomicReferenceFieldUpdaterImpl的神秘面纱
AtomicReferenceFieldUpdaterImpl(final Class<T> tclass,
final Class<V> vclass,
final String fieldName,
final Class<?> caller) {
final Field field;
final Class<?> fieldClass;
final int modifiers;
try {
//通过反射获取待更新对象的field域
field = AccessController.doPrivileged(
new PrivilegedExceptionAction<Field>() {
public Field run() throws NoSuchFieldException {
return tclass.getDeclaredField(fieldName);
}
});
modifiers = field.getModifiers();
sun.reflect.misc.ReflectUtil.ensureMemberAccess(
caller, tclass, null, modifiers);
ClassLoader cl = tclass.getClassLoader();
ClassLoader ccl = caller.getClassLoader();
if ((ccl != null) && (ccl != cl) &&
((cl == null) || !isAncestor(cl, ccl))) {
sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
}
fieldClass = field.getType();
} catch (PrivilegedActionException pae) {
throw new RuntimeException(pae.getException());
} catch (Exception ex) {
throw new RuntimeException(ex);
}
//检查类型是否符合
if (vclass != fieldClass)
throw new ClassCastException();
//检查是否是属于引用类型
if (vclass.isPrimitive())
throw new IllegalArgumentException("Must be reference type");
//检查是否是volatile变量
if (!Modifier.isVolatile(modifiers))
throw new IllegalArgumentException("Must be volatile type");
// Access to protected field members is restricted to receivers only
// of the accessing class, or one of its subclasses, and the
// accessing class must in turn be a subclass (or package sibling)
// of the protected member's defining class.
// If the updater refers to a protected field of a declaring class
// outside the current package, the receiver argument will be
// narrowed to the type of the accessing class.
this.cclass = (Modifier.isProtected(modifiers) &&
tclass.isAssignableFrom(caller) &&
!isSamePackage(tclass, caller))
? caller : tclass;
this.tclass = tclass;
this.vclass = vclass;
this.offset = U.objectFieldOffset(field);
}
代码虽然很多,但核心思想却比较简单,获取并判断field是否可以进行更新,主要检查了访问权限,检查了filed的类型,检查了field是否是volatile
类型。最后的目的是为了拿到这个field的offset偏移值,拿到offset后,后面的更新也就水到渠成了,值得注意的是由于采用了反射,可能存在类型不一致的问题,因此做了一些check。
public final boolean compareAndSet(T obj, V expect, V update) {
accessCheck(obj);
valueCheck(update);
return U.compareAndSwapObject(obj, offset, expect, update);
}
private final void accessCheck(T obj) {
if (!cclass.isInstance(obj))
throwAccessCheckException(obj);
}
private final void valueCheck(V v) {
if (v != null && !(vclass.isInstance(v)))
throwCCE();
}
public final V getAndSet(T obj, V newValue) {
accessCheck(obj);
valueCheck(newValue);
return (V)U.getAndSetObject(obj, offset, newValue);
}