前言
AtomicIntegerArray提供了对数组元素的原子操作,与其他非数组的原子类相比,它的成员不是volatile的而是final的了。
JUC框架 系列文章目录
成员
java"> private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final int base = unsafe.arrayBaseOffset(int[].class);//获得第一个元素的起始位置
private static final int shift;
private final int[] array; //注意不是volatile的
static {
int scale = unsafe.arrayIndexScale(int[].class); //返回数组元素类型占几个字节,这里int是4字节
if ((scale & (scale - 1)) != 0) //scale必须为2的幂
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);//返回二进制中第一个1之前的0的个数
}
private long checkedByteOffset(int i) {
if (i < 0 || i >= array.length)
throw new IndexOutOfBoundsException("index " + i);
return byteOffset(i);
}
private static long byteOffset(int i) {
return ((long) i << shift) + base;
}
- 既然是数组,所以要操作数组元素。而数组的内部存储方式有可能是先存size,再存各个元素,所以第一个元素的起始地址需要通过
arrayBaseOffset
确认。根据base地址,再加上(元素个数-1) * 类型大小
,就得到某个元素的起始地址。 arrayIndexScale
得到数组元素类型的大小,这里是int是4字节,所以返回4。scale为4。(scale & (scale - 1)) != 0
这个判断,只有当scale为2的幂时,scale & (scale - 1)
就会刚好等于0,然后判断为false。- scale和shift的关系是
s
c
a
l
e
=
2
s
h
i
f
t
scale = 2^{shift}
scale=2shift。运算过程是这样的:因为scale是int型,所以使用
Integer.numberOfLeadingZeros()
(见注释),scale为4即0b100
,int型总共32个bit,所以第一个1前面有29个0,numberOfLeadingZeros返回29。又因为使用的Integer.numberOfLeadingZeros()
,所以前面那个数为32 -1。最终,31 - 29 = 2,即最终shift = 2。 - 前面说到需要计算
(元素个数-1) * 类型大小
,现在看byteOffset
函数的实现,你会发现 i < < s h i f t = i ∗ 2 s h i f t = i ∗ s c a l e i << shift = i*2^{shift } = i*scale i<<shift=i∗2shift=i∗scale,当然前提是左移没有超过最高位。而i
传入的都是索引(元素的第几个减1),所以((long) i << shift) + base
刚好就是(元素个数-1) * 类型大小
了。 byteOffset
函数,传入元素索引,返回这个数组元素的起始地址。
构造器
java"> public AtomicIntegerArray(int length) {
array = new int[length];
}
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}
- 前者通过数组的长度,新建一个数组。
- 后者通过克隆一个传入数组。也算是新建数组了。
常用操作
java"> public final int length() {
return array.length;
}
public final int get(int i) {
return getRaw(checkedByteOffset(i));
}
private int getRaw(long offset) {
return unsafe.getIntVolatile(array, offset);
}
public final void set(int i, int newValue) {
unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}
public final void lazySet(int i, int newValue) {
unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
}
- 由于array成员不是volatile的,所以
get/set
时,都借助了unsafe来达到volatile语义的get/set
。 lazySet
则不保证可见性。
java"> public final int getAndSet(int i, int newValue) {
return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
}
//Unsafe.java
public final int getAndSetInt(Object o, long offset, int newValue) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, newValue));
return v;
}
getAndSet
直接设置新值,但getAndSetInt
也是循环加CAS,因为需要正确返回设置成功时的旧值。getAndSet
顾名思义,获得旧值,设置为新值。
java"> public final boolean compareAndSet(int i, int expect, int update) {
return compareAndSetRaw(checkedByteOffset(i), expect, update);
}
private boolean compareAndSetRaw(long offset, int expect, int update) {
return unsafe.compareAndSwapInt(array, offset, expect, update);
}
- 最终使用
unsafe.compareAndSwapInt
,反正将array当成普通对象,直接从地址偏移来进行CAS操作。
java"> public final int getAndIncrement(int i) {
return getAndAdd(i, 1); //获得旧值,所以直接结果就行
}
public final int getAndDecrement(int i) {
return getAndAdd(i, -1);
}
public final int getAndAdd(int i, int delta) { //此函数被复用
return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
}
public final int incrementAndGet(int i) {
return getAndAdd(i, 1) + 1; //获得新值,所以需要加1
}
public final int decrementAndGet(int i) {
return getAndAdd(i, -1) - 1; //获得新值,所以需要减1
}
public final int addAndGet(int i, int delta) {
return getAndAdd(i, delta) + delta; //获得新值,所以需要加delta
}
以上就是一些常用操作。
java"> public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset); //Volatile语义地获得i索引的值
next = updateFunction.applyAsInt(prev); //调用int的一元方程
} while (!compareAndSetRaw(offset, prev, next));
return prev;//返回旧值
}
public final int updateAndGet(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));
return next;//返回新值
}
public final int getAndAccumulate(int i, int x,
IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
next = accumulatorFunction.applyAsInt(prev, x); //调用int的二元方程
} while (!compareAndSetRaw(offset, prev, next));
return prev;
}
public final int accumulateAndGet(int i, int x,
IntBinaryOperator accumulatorFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
prev = getRaw(offset);
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSetRaw(offset, prev, next));
return next;
}
以上配合两种函数式接口,实现了些常用操作。
java"> public String toString() {
int iMax = array.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(getRaw(byteOffset(i)));//注意,也Volatile语义地获得i索引的值
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}