AtomicIntegerArray
- AtomicIntegerArray 的作用是什么?
- AtomicIntegerArray 多线程情况下是如何保证安全的?
- 为什么 AtomicIntegerArray 的数组要定义为 final 类型?
AtomicIntegerArray 的作用是什么?
在多线程环境下,保证数组元素运算的一致性。下面我给了一个例子,说明 AtomicIntegerArray 的作用
@Test
public void test() {
int[] arr = new int[10];
for (int i = 0; i < 10000; i++) {
new Thread(()->{
for (int j = 0; j < arr.length; j++) {
arr[j] += 1;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终结果
for (int i = 0; i < arr.length; i++) {
System.out.println("Element at index " + i + ": " + arr[i]); // 会出现问题,可能某几个元素的最终结果不为 10000
}
}
@Test
public void testAddAndGet() {
int[] arr = new int[10];
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
for (int i = 0; i < 10000; i++) {
new Thread(()->{
for (int j = 0; j < arr.length; j++) {
atomicIntegerArray.addAndGet(j,1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终结果
for (int i = 0; i < arr.length; i++) {
System.out.println("Element at index " + i + ": " + atomicIntegerArray.get(i)); // 不会出现问题
}
}
AtomicIntegerArray 多线程情况下是如何保证安全的?
首先查看源码,可以看到在 AtomicIntegerArray 定义了一个 final 的数组,
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;
static {
int scale = unsafe.arrayIndexScale(int[].class);
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
shift = 31 - Integer.numberOfLeadingZeros(scale);
}
private static long byteOffset(int i) {
return ((long) i << shift) + base; // ((long) i << shift) + base 是(元素个数-1) * 类型大小,要使用 CAS 就必须知道对象在函数中的偏移量
}
由于数组元素类型没有定义成 volatile ,所以在进行实际的修改操作时,必须使用 带 volatile 的修改方法。其余的方法与 AtomicInteger 的方法类型
public final void set(int i, int newValue) {
unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
}
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;
}
为什么 AtomicIntegerArray 的数组要定义为 final 类型?
将数组定义为 final 类型可以确保一旦初始化完成,其内容就不能再被修改。这样,当多个线程同时访问和修改 AtomicIntegerArray 时,可以确保每个线程看到的数组内容是一致的,不会因为某个线程在修改数组时导致其他线程看到不一致的状态。