JDK12源码分析_03 AtomicIntegerArray 和 AtomicIntegerFieldUpdater、AtomicReferenceFieldUpdater 源码分析
AtomicIntegerArray
AtomicIntegerArray 、AtomicLongArray 、AtomicReferenceArray 这三个类都是数组型原子操作,这里就拿AtomicIntegerArray 举个例子吧。
private static final VarHandle AA
= MethodHandles.arrayElementVarHandle(int[].class);
private final int[] array;
这里我们可以看到底层的原子操作是通过VarHandle 来进行的,数据保存在一个 final int[] array里面,但是它本身并不是原子的volatile修饰的。
public AtomicIntegerArray(int length) {
array = new int[length];
}
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
看构造函数可以发现,传入的值必须严格是int[]基本类型的数据,不能是对象。
public final int get(int i) {
return (int)AA.getVolatile(array, i);
}
public final void set(int i, int newValue) {
AA.setVolatile(array, i, newValue);
}
这里的get set方法就可以看出,该数组的每一个元素都是原子性的Volatile。
/**
* Atomically sets the element at index {@code i} to {@code
* newValue} if the element's current value {@code == expectedValue},
* with memory effects as specified by {@link VarHandle#compareAndSet}.
*
* @param i the index
* @param expectedValue the expected value
* @param newValue the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int i, int expectedValue, int newValue) {
return AA.compareAndSet(array, i, expectedValue, newValue);
}
compareAndSet 本质是对数组里面每一个元素进行CAS操作。如果元素的当前值是{@code == expectedValue},则原子性地将索引{@code i}处的元素设置为{@code newValue},内存效果由{@link VarHandle#compareAndSet}指定。我们来看一下官方文档。
Atomically sets the element at index i to newValue if the element’s current value == expectedValue, with memory effects as specified by VarHandle.compareAndSet(java.lang.Object…).
我的个人翻译:如果元素的当前值== expectedValue,则原子性地将索引i处的元素设置为newValue,并使用VarHandle.compareAndSet(java.lang.Object…)指定的内存效果。
接下来我们来看一个特别的方法
public final int accumulateAndGet(int i, int x,
IntBinaryOperator accumulatorFunction) {
int prev = get(i), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.applyAsInt(prev, x);
if (weakCompareAndSetVolatile(i, prev, next))
return next;
haveNext = (prev == (prev = get(i)));
}
}
accumulateAndGet,还有一个方法getAndAccumulate和他类似。IntBinaryOperator 是函数式接口,需要自己实现applyAsInt方法。
Atomically updates (with memory effects as specified by {@link VarHandle#compareAndSet}) the element at index {@code i} with the results of applying the given function to the current and given values, returning the previous value. The function should be side-effect-free, since it may be re-applied when attempted updates fail due to contention among threads. The function is applied with the current value of the element at index {@code i} as its first argument, and the given update as the second argument.
我的翻译如下:原子性地更新(使用{@link VarHandle#compareAndSet}指定的内存效果)索引{@code i}中的元素,并将给定函数应用于当前值和给定值,返回前一个值。该函数应该是自旋作用的,因为当尝试的更新由于线程间的争用而失败时,可以重新应用该函数。函数的作用是:索引{@code i}处元素的当前值作为第一个参数,给定的update作为第二个参数。
接下来我们写一个实战方法:
// 陈涛版权所有,禁止转载,QQ邮箱:1393899065@qq.com
public static void main(String[] args) throws Exception {
AtomicIntegerArray array = new AtomicIntegerArray(10);
array.set(0, 1);
array.set(1, 2);
int k = array.accumulateAndGet(1, 3, new IntBinaryOperator() {
public int applyAsInt(int left, int right) {
return left + right;
}
});
System.out.println(k);
}
这里我们给数组[0]元素设置为1,数组[1]元素设置为2,accumulateAndGet 把数组[1]元素和3相加并CAS替换掉数组[1]元素,相当于2+3,所以最后输出5。
AtomicIntegerFieldUpdater
AtomicIntegerFieldUpdater 、AtomicLongFieldUpdater 、AtomicReferenceFieldUpdater,这三个原理相似,我们重点分析第1个。
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。此类用于原子数据结构,该结构中同一节点的几个字段都独立受原子更新控制。newUpdater是核心方法。下面我们写一个例子
// 陈涛版权所有,禁止转载,QQ邮箱:1393899065@qq.com
public class User {
volatile int id;
volatile String name;
// get set 方法。。。
}
public class TestAtomicIntegerFieldUpdater {
public static void main(String[] args) {
AtomicIntegerFieldUpdater<User> updater = AtomicIntegerFieldUpdater.newUpdater(User.class, "id");
User user = new User();
user.setId(10);
user.setName("name");
updater.compareAndSet(user, 10, 11);
System.out.println("id="+user.getId());
}
}
这里输出值为id=11,我们可以看到newUpdater可以对User 实例对象里面的 volatile int id 字段进行CAS更新,但是这里要求 id 必须为 volatile int 类型的。问个问题,如果改成 Integer,会怎么样呢?
Exception in thread "main" java.lang.IllegalArgumentException: Must be integer type
at java.base/java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:417)
at java.base/java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:94)
at my.TestAtomicIntegerFieldUpdater.main(TestAtomicIntegerFieldUpdater.java:8)
IllegalArgumentException: Must be integer type,这句话写的很清楚,必须使用原始类型。也必须使用 volatile 来修饰。对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。
AtomicReferenceFieldUpdater
原理和上一个相似,这里我们写个例子。
// 陈涛版权所有,禁止转载,QQ邮箱:1393899065@qq.com
public static void main(String[] args) {
AtomicReferenceFieldUpdater<User, String> updater = AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");
User user = new User();
user.setId(10);
user.setName("myName1");
updater.compareAndSet(user, "myName1", "myName2");
System.out.println("name=" + user.getName());
}
这里输出值为name=myName2,依旧对对象的类型有要求 volatile String name,不可以是private,具体原理就不再阐述了。