Java 原子类 AtomicIntegerArray 数组原子操作
下文笔者讲述java原子类数组的简介说明,如下所示
AtomicIntegerArray 核心特性
`AtomicIntegerArray` 是 Java `java.util.concurrent.atomic` 包下提供的数组原子操作工具类,
专门用于实现 `int` 类型数组的线程安全原子操作,其核心特性如下:
1.线程安全:无需手动加锁(如 `synchronized` 或 `Lock`),
底层通过 CAS(Compare-And-Swap)+ Unsafe 类 实现无锁原子操作,
避免多线程竞争下的数据不一致问题;
2.数组聚焦:仅针对 `int` 类型数组提供原子操作,
支持对数组指定索引位置的元素进行原子更新、查询等操作;
3.无锁优化:相比加锁实现的数组线程安全,
无锁机制减少了锁竞争和线程阻塞的开销,在高并发场景下性能更优;
4.原子性保证:对数组单个元素的操作(如更新、累加、比较替换)具有原子性,
确保多线程同时操作数组时,每个元素的修改都是完整且有序的。
二、AtomicIntegerArray 常用API与核心原子操作
`AtomicIntegerArray` 的
API 围绕数组索引元素的原子操作设计,
分为查询、更新、累加、比较替换四大类
1. 构造方法(初始化原子数组)
| 构造方法 | 说明 |
| `AtomicIntegerArray(int length)` | 创建一个长度为 `length` 的空原子数组,初始值均为 0 |
| `AtomicIntegerArray(int[] array)` | 传入一个普通 `int` 数组, 创建一个包含相同元素的原子数组(注意:是拷贝数组元素,而非引用) |
2. 基础查询操作(非原子性,但无需原子性)
| 方法 | 说明 |
| `int length()` | 返回原子数组的长度 |
| `int get(int index)` | 获取数组指定 `index` 位置的c元素值(单次查询无需原子性,返回当前最新值) |
| `void get(int[] dst)` | |将原子数组的所有元素拷贝 到目标数组 `dst` 中 |
3. 原子更新操作
| 方法 | 说明 | |
| `void set(int index, int newValue)` | 直接设置数组 `index` 位置的元素值为 `newValue` | (普通更新,不保证可见性?不,底层通过 Unsafe 保证可见性) |
| `void lazySet(int index, int newValue)` | 延迟设置数组 `index` 位置的值, | 不保证立即对其他线程可见(适用于无需立即同步的场景,性能略高) |
| `int getAndSet(int index, int newValue)` | 原子操作:先获取数组 `index` 位置的旧值, | 再设置为 `newValue`,返回旧值 |
4. 原子累加/自增自减操作
| 方法 | 说明 |
| `int getAndIncrement(int index)` | 原子自增:先获取数组 `index` 位置的旧值,再将该位置值 +1,返回旧值 |
| `int incrementAndGet(int index)` | 原子自增:先将数组 `index` 位置值 +1,再获取新值,返回新值 |
| `int getAndDecrement(int index)` | 原子自减:先获取数组 `index` 位置的旧值,再将该位置值 -1,返回旧值 |
| `int decrementAndGet(int index)` | 原子自减:先将数组 `index` 位置值 -1,再获取新值,返回新值 |
| `int getAndAdd(int index, int delta)` | 原子累加:先获取数组 `index` 位置的旧值,再将该位置值 + `delta`(可正可负),返回旧值 |
| `int addAndGet(int index, int delta)` | 原子累加:先将数组 `index` 位置值 + `delta`,再获取新值,返回新值 |
5. 原子比较并替换操作(CAS 核心操作)
| 方法 | 说明 |
| `boolean compareAndSet(int index, int expect, int update)` | 原子 CAS 操作: 若数组 `index` 位置的当前值等于 `expect`, 则将其更新为 `update`,更新成功返回 `true`, 失败返回 `false`(核心方法,保证原子性和可见性) |
| `boolean weakCompareAndSet(int index, int expect, int update)` | 弱 CAS 操作:功能与 `compareAndSet` 基本一致, 但不保证多线程下的有序性,仅适用于单线程或无需严格有序的场景,性能略高 |
6. 函数式原子更新操作(JDK 8+ 新增)
| 方法 | 说明 |
| `int getAndUpdate(int index, IntUnaryOperator updateFunction)` | 原子更新: 先获取数组 `index` 位置旧值, 通过函数 `updateFunction` 计算新值,设置后返回旧值 |
| `int updateAndGet(int index, IntUnaryOperator updateFunction)` | 原子更新:先通过函数 `updateFunction` 计算新值,设置后返回新值 |
| `int getAndAccumulate(int index, int x, IntBinaryOperator accumulatorFunction)` | 原子累加更新:先获取旧值, 结合参数 `x` 通过二元函数计算新值,设置后返回旧值 |
| `int accumulateAndGet(int index, int x, IntBinaryOperator accumulatorFunction)` | 原子累加更新: 先结合参数 `x` 通过二元函数计算新值,设置后返回新值 |
数组原子操作(自增、CAS、getAndSet)示例代码
演示多线程对数组同一索引和不同索引的原子操作,验证线程安全性:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayBasicDemo {
// 初始化长度为5的原子数组,初始值为0
private static final AtomicIntegerArray ATOMIC_ARRAY = new AtomicIntegerArray(5);
// 线程数
private static final int THREAD_COUNT = 10;
// 每个线程执行的操作次数
private static final int OP_COUNT = 1000;
public static void main(String[] args) throws InterruptedException {
// 线程1:对索引0进行原子自增
Runnable incrementTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
ATOMIC_ARRAY.incrementAndGet(0); // 索引0自增,返回新值
}
};
// 线程2:对索引1进行原子累加(每次加2)
Runnable addTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
ATOMIC_ARRAY.getAndAdd(1, 2); // 索引1累加2,返回旧值
}
};
// 线程3:对索引2进行CAS操作
Runnable casTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
int oldValue;
int newValue;
// 循环CAS,确保更新成功
do {
oldValue = ATOMIC_ARRAY.get(2);
newValue = oldValue + 1;
} while (!ATOMIC_ARRAY.compareAndSet(2, oldValue, newValue));
}
};
// 线程4:对索引3进行getAndSet操作
Runnable setTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
ATOMIC_ARRAY.getAndSet(3, ATOMIC_ARRAY.get(3) + 1); // 先获取旧值,再设置新值
}
};
// 创建并启动线程
Thread[] threads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
if (i % 4 == 0) {
threads[i] = new Thread(incrementTask, "自增线程-" + i);
} else if (i % 4 == 1) {
threads[i] = new Thread(addTask, "累加线程-" + i);
} else if (i % 4 == 2) {
threads[i] = new Thread(casTask, "CAS线程-" + i);
} else {
threads[i] = new Thread(setTask, "Set线程-" + i);
}
threads[i].start();
}
// 等待所有线程执行完毕
for (Thread thread : threads) {
Thread.join();
}
// 打印最终结果
System.out.println("索引0最终值(预期:101000=10000):" + ATOMIC_ARRAY.get(0));
System.out.println("索引1最终值(预期:1010002=20000):" + ATOMIC_ARRAY.get(1));
System.out.println("索引2最终值(预期:101000=10000):" + ATOMIC_ARRAY.get(2));
System.out.println("索引3最终值(预期:101000=10000):" + ATOMIC_ARRAY.get(3));
System.out.println("索引4最终值(未操作,预期:0):" + ATOMIC_ARRAY.get(4));
}
}
运行结果(线程安全,结果符合预期):
索引0最终值(预期:101000=10000):10000
索引1最终值(预期:1010002=20000):20000
索引2最终值(预期:101000=10000):10000
索引3最终值(预期:101000=10000):10000
索引4最终值(未操作,预期:0):0
例2:函数式原子更新操作(JDK 8+)
`getAndUpdate` 和 `accumulateAndGet` 的使用,
实现自定义逻辑的原子更新:
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
public class AtomicIntegerArrayFunctionalDemo {
public static void main(String[] args) {
// 传入普通数组创建原子数组
int[] initArray = {1, 2, 3, 4, 5};
AtomicIntegerArray atomicArray = new AtomicIntegerArray(initArray);
// 1. 函数式更新:索引0的值翻倍(x -> x 2)
IntUnaryOperator doubleOperator = x -> x 2;
int oldValue0 = atomicArray.getAndUpdate(0, doubleOperator);
int newValue0 = atomicArray.get(0);
System.out.println("索引0:旧值=" + oldValue0 + ",新值=" + newValue0 + "(预期:1→2)");
// 2. 函数式累加:索引1的值加上10((x, y) -> x + y,y=10)
IntBinaryOperator addOperator = (x, y) -> x + y;
int newValue1 = atomicArray.accumulateAndGet(1, 10, addOperator);
System.out.println("索引1:新值=" + newValue1 + "(预期:2+10=12)");
// 3. 自定义逻辑:索引2的值取平方后加5
int oldValue2 = atomicArray.getAndUpdate(2, x -> x x + 5);
int newValue2 = atomicArray.get(2);
System.out.println("索引2:旧值=" + oldValue2 + ",新值=" + newValue2 + "(预期:3→3²+5=14)");
}
}
运行结果
索引0:旧值=1,新值=2(预期:1→2)
索引1:新值=12(预期:2+10=12)
索引2:旧值=3,新值=14(预期:3→3²+5=14)
例3:对比普通数组(非线程安全)与 AtomicIntegerArray(线程安全)
多线程操作普通数组会出现数据不一致,
而原子数组可保证线程安全:
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicVsNormalArrayDemo {
// 普通数组
private static final int[] NORMAL_ARRAY = new int[1];
// 原子数组
private static final AtomicIntegerArray ATOMIC_ARRAY = new AtomicIntegerArray(1);
// 线程数
private static final int THREAD_COUNT = 5;
// 每个线程操作次数
private static final int OP_COUNT = 10000;
public static void main(String[] args) throws InterruptedException {
// 测试普通数组(非线程安全)
Runnable normalTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
NORMAL_ARRAY[0]++; // 非原子操作,多线程竞争会导致数据丢失
}
};
Thread[] normalThreads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
normalThreads[i] = new Thread(normalTask, "普通数组线程-" + i);
normalThreads[i].start();
}
for (Thread thread : normalThreads) {
thread.join();
}
System.out.println("普通数组索引0最终值(预期:510000=50000):" + NORMAL_ARRAY[0]); // 结果小于50000,数据不一致
// 测试原子数组(线程安全)
Runnable atomicTask = () -> {
for (int i = 0; i < OP_COUNT; i++) {
ATOMIC_ARRAY.incrementAndGet(0); // 原子自增,无数据丢失
}
};
Thread[] atomicThreads = new Thread[THREAD_COUNT];
for (int i = 0; i < THREAD_COUNT; i++) {
atomicThreads[i] = new Thread(atomicTask, "原子数组线程-" + i);
atomicThreads[i].start();
}
for (Thread thread : atomicThreads) {
thread.join();
}
System.out.println("原子数组索引0最终值(预期:510000=50000):" + ATOMIC_ARRAY.get(0)); // 结果等于50000,线程安全
}
}
运行结果(普通数组数据不一致,原子数组线程安全):
普通数组索引0最终值(预期:510000=50000):48765(每次结果不同,小于50000)
原子数组索引0最终值(预期:510000=50000):50000
AtomicIntegerArray 核心优势及注意事项
1. 核心优势
- 线程安全无需手动加锁:底层 CAS 无锁机制,
避免`synchronized` 锁的竞争开销和线程阻塞,
高并发场景下性能更优;
- 粒度更细:
针对数组单个索引元素进行原子操作,
相比对整个数组加锁,粒度更细,并发度更高;
- API 简洁易用:
提供丰富的原子操作 API,
无需手动实现 CAS 循环,
降低线程安全编程的复杂度;
- 支持函数式更新:
JDK 8+ 新增的函数式 API,
支持自定义更新逻辑,
灵活性更高。
2. 使用注意事项
- 索引越界问题:
所有操作都需要指定数组索引,
索引必须在 `[0, length-1]` 范围内,
否则会抛出 `IndexOutOfBoundsException`;
- 数组是固定长度:`AtomicIntegerArray` 创建后长度不可修改,
若需要动态扩容,需手动创建新的原子数组并拷贝数据;
- 仅支持 int 类型:若需要操作 `long`/`boolean`/`reference` 类型数组,
需使用对应的 `AtomicLongArray`/`AtomicBooleanArray`/`AtomicReferenceArray`;
- CAS 可能失败:`compareAndSet` 方法可能因竞争导致更新失败,
若需要确保更新成功,需配合循环使用(如示例1中的 CAS 任务);
- 不保证数组整体的原子性:`AtomicIntegerArray` 仅保证单个索引元素操作的原子性,
若需要对多个索引元素进行原子性的批量操作,仍需手动加锁。
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


