AtomicIntegerArray类源码剖析

AtomicIntegerArray类源码剖析

一、AtomicIntegerArray类的简介

AtomicIntegerArray类是java.util.concurrent.atomic包下的提供了可以以原子方式读取和写入底层int数组的操作,并且还包含高级原子操作的并发数组类

image-20231115124839539

二、AtomicIntegerArray类的属性

1
2
3
4
5
6
7
8
// 数组的原子更新依靠的就是底层Unsafe对象的CAS操作
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;

我们好奇的是shift是怎么计算的,它其实在类的静态代码块中计算得到,简单理解就是数组元素占用的字节大小的对数,即整型4字节计算的shift就是2。

1
2
3
4
5
6
7
8
9
10
static {
// 获取数组中单个元素占用的字节数
int scale = unsafe.arrayIndexScale(int[].class);
// 判断数据类型缩放是不是2的幂
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
// scale的二进制是00000000 00000000 00000000 00000100
// shift = 31 - 29 = 2,即左移2位
shift = 31 - Integer.numberOfLeadingZeros(scale);
}

三、AtomicIntegerArray类的特殊方法

AtomicIntegerArray中大多数方法获取指定下标元素的数组偏移量都是靠checkedByteOffset(int i)方法来进行获取的,而checkedByteOffset(int i)又是靠byteOffset(int i)来实现的。

1
2
3
4
5
6
7
8
9
10
11
// 检查下标i是否越界,并且返回其在数组中从base开始的偏移量
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;
}

四、AtomicIntegerArray类的构造方法

1
2
3
4
5
6
7
8
9
10
// 创建一个给定长度的新AtomicIntegerArray,所有元素最初为零
public AtomicIntegerArray(int length) {
array = new int[length];
}

// 创建一个新的AtomicIntegerArray,其长度与给定数组相同,并且所有元素都复制自给定数组
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}

五、AtomicIntegerArray类的重要方法

1.简单的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// 获取下标为i的元素
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);
}

// 根据下标获取数组中的元素的值,并且将此数组对应下标的值变为新值newValue
public final int getAndSet(int i, int newValue) {
return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
}

// CAS的原理
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);
}

// 先获取指定下标的值,然后再让此值加一
public final int getAndIncrement(int i) {
return getAndAdd(i, 1);
}

// 先获取指定下标的值,然后再让此值减一
public final int getAndDecrement(int i) {
return getAndAdd(i, -1);
}

//先对指定下标的值加一,然后返回加一后的值
public final int incrementAndGet(int i) {
return getAndAdd(i, 1) + 1;
}

//先对指定下标的值减一,然后返回减一后的值
public final int decrementAndGet(int i) {
return getAndAdd(i, -1) - 1;
}

// 获取指定下标的值,并且在指定下标的值的基础上加delta
public final int getAndAdd(int i, int delta) {
return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
}

2.复杂的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 使用给定函数updateFunction的结果以原子方式更新索引i处的元素,返回以前的值。该函数应该没有副作用,因为当尝试更新由于线程之间的争用而失败时,可能会重新应用它。
public final int getAndUpdate(int i, IntUnaryOperator updateFunction) {
long offset = checkedByteOffset(i);
int prev, next;
do {
// 数组指定下标上的原值
prev = getRaw(offset);
// 数组指定下标上的新值
next = updateFunction.applyAsInt(prev);
} while (!compareAndSetRaw(offset, prev, next));// CAS更新数组指定下标的值
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;
}

// 使用将给定函数accumulatorFunction.applyAsInt应用于当前值和给定值的结果,以原子方式更新索引i处的元素,并返回前一个值。该函数应该没有副作用,因为当尝试更新由于线程之间的争用而失败时,可能会重新应用它。应用该函数时,索引i处的当前值作为其第一个参数,给定值作为第二个参数。
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);
} while (!compareAndSetRaw(offset, prev, next));// CAS更新数组指定下标的值
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;
}

3.打印方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)));
if (i == iMax)
return b.append(']').toString();
b.append(',').append(' ');
}
}

4.依赖的底层方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Unsafe类的本地方法getIntVolatile
public native int getIntVolatile(Object o, long offset);
// Unsafe类的本地方法putIntVolatile
public native void putIntVolatile(Object o, long offset, int x);
// Unsafe类的CAS操作+自旋重试
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;
}
// Unsafe类的CAS操作+自旋重试
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!compareAndSwapInt(o, offset, v, v + delta));
return v;
}
// Unsafe类的CAS操作
public final native boolean compareAndSwapInt(Object o, long offset,
int expected,
int x);

总结,AtomicIntegerArray类底层依赖的就是Unfase的数组CAS+失败自旋重试完成原子更新的!