如何在多线程环境下判断AtomicIntegerArray是否已经初始化?
下文笔者讲述多线程环境下判断AtomicIntegerArray是否初始化的方法及示例分享,如下所示
多线程判断方法
多线程上判断,我们可采用以下步骤处理
1. 第一次非空检查:无锁快速判断,
绝大多数情况下直接返回已初始化对象,避免进入同步代码块;
2. 同步锁:保证只有一个线程进入初始化逻辑,
避免重复创建;
3. 第二次非空检查:防止多个线程等待锁后,
再次重复初始化;
4. `volatile` 修饰引用:保证引用的可见性,
禁止指令重排,避免其他线程获取到半初始化的对象。
例
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayDCLInit {
// 关键:用volatile修饰AtomicIntegerArray引用,保证可见性和禁止指令重排
private static volatile AtomicIntegerArray atomicArray;
/
多线程安全的判断与获取方法
/
public static AtomicIntegerArray getAtomicArray() {
// 第一次检查:无锁快速判断,避免不必要的锁竞争
if (atomicArray == null) {
// 同步锁:保证只有一个线程进入初始化流程
synchronized (AtomicIntegerArrayDCLInit.class) {
// 第二次检查:防止等待锁的线程重复初始化
if (atomicArray == null) {
System.out.println(Thread.currentThread().getName() + ":开始初始化AtomicIntegerArray");
// 初始化原子数组(此处可根据业务需求指定长度或传入初始数组)
atomicArray = new AtomicIntegerArray(10);
System.out.println(Thread.currentThread().getName() + ":AtomicIntegerArray初始化完成");
}
}
}
// 若已初始化,直接返回;若未初始化,执行完上面的逻辑后返回
return atomicArray;
}
/
多线程环境下的测试方法
/
public static void main(String[] args) throws InterruptedException {
// 启动20个线程,并发判断并获取AtomicIntegerArray
for (int i = 0; i < 20; i++) {
new Thread(() -> {
AtomicIntegerArray array = getAtomicArray();
System.out.println(Thread.currentThread().getName() + ":获取到原子数组,长度:" + array.length());
}, "工作线程-" + i).start();
}
// 等待所有线程执行完毕
Thread.sleep(2000);
System.out.println("所有线程执行完成,原子数组最终状态:" + (atomicArray != null ? "已初始化" : "未初始化"));
}
}
运行结果(线程安全,仅一次初始化)
工作线程-0:开始初始化AtomicIntegerArray
工作线程-0:AtomicIntegerArray初始化完成
工作线程-0:获取到原子数组,长度:10
工作线程-1:获取到原子数组,长度:10
工作线程-2:获取到原子数组,长度:10
(后续所有线程均获取到已初始化的数组,无重复初始化)
所有线程执行完成,原子数组最终状态:已初始化
方案2:使用静态内部类(懒汉式单例,更简洁安全)
利用 Java 类加载机制的线程安全性,
通过静态内部类实现 `AtomicIntegerArray` 的懒加载(按需初始化),
无需手动加锁,天然线程安全。
核心原理
- 静态内部类 `AtomicArrayHolder` 不会在外部类加载时初始化,
只有当调用 `getAtomicArray()` 方法时,才会加载内部类并初始化 `atomicArray`;
- Java 类加载器在加载静态内部类时,会保证初始化操作的原子性,
多个线程同时调用时,只会有一个线程触发类加载,避免重复初始化;
- 无需手动使用 `volatile` 和同步锁,
由 JVM 底层保证可见性和线程安全。
例
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicIntegerArrayStaticInnerInit {
// 私有化构造方法,避免外部实例化
private AtomicIntegerArrayStaticInnerInit() {}
/
静态内部类:持有AtomicIntegerArray的实例,利用类加载机制保证线程安全
/
private static class AtomicArrayHolder {
// 初始化AtomicIntegerArray,类加载时仅执行一次
private static final AtomicIntegerArray ATOMIC_ARRAY = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
}
/
多线程安全的判断与获取方法(判断逻辑隐含在类加载中)
/
public static AtomicIntegerArray getAtomicArray() {
// 若内部类未加载,则触发加载并初始化ATOMIC_ARRAY;若已加载,直接返回已有实例
return AtomicArrayHolder.ATOMIC_ARRAY;
}
/
测试多线程环境下的初始化判断
/
public static void main(String[] args) throws InterruptedException {
// 启动15个线程,并发获取原子数组
for (int i = 0; i < 15; i++) {
new Thread(() -> {
AtomicIntegerArray array = getAtomicArray();
System.out.println(Thread.currentThread().getName() + ":原子数组已初始化,索引0的值:" + array.get(0));
// 验证线程安全:原子自增索引0的值
array.incrementAndGet(0);
}, "测试线程-" + i).start();
}
Thread.sleep(1000);
// 打印最终结果,验证原子操作有效性
System.out.println("最终索引0的值:" + getAtomicArray().get(0) + "(预期:1+15=16)");
}
}
运行结果(天然线程安全,无重复初始化)
测试线程-0:原子数组已初始化,索引0的值:1
测试线程-1:原子数组已初始化,索引0的值:2
测试线程-2:原子数组已初始化,索引0的值:3
...
测试线程-14:原子数组已初始化,索引0的值:15
最终索引0的值:16(预期:1+15=16)
方案3:使用 AtomicReference(无锁方案,灵活度高)
使用`AtomicReference` 封装 `AtomicIntegerArray` 引用,
利用CAS原子操作实现无锁化的初始化判断与创建,
避免锁竞争带来的性能开销。
核心原理
- 用`AtomicReference` 存储 `AtomicIntegerArray`引用,
保证引用更新的原子性;
- 通过`compareAndSet(null, newArray)` 实现“判断-初始化”的原子操作:
只有当引用为 `null`时,
才会创建新的 `AtomicIntegerArray` 并更新引用;
- 无需使用同步锁,
通过 CAS 操作保证线程安全,适合高并发场景。
例
import java.util.concurrent.atomic.AtomicIntegerArray;
import java.util.concurrent.atomic.AtomicReference;
public class AtomicIntegerArrayAtomicRefInit {
// 用AtomicReference封装AtomicIntegerArray引用,初始为null
private static final AtomicReference<AtomicIntegerArray> ATOMIC_ARRAY_REF = new AtomicReference<>(null);
/
无锁化线程安全判断与初始化方法
/
public static AtomicIntegerArray getAtomicArray() {
// 先获取当前引用
AtomicIntegerArray atomicArray = ATOMIC_ARRAY_REF.get();
// 第一次判断:若未初始化,进入CAS逻辑
if (atomicArray == null) {
// 创建新的AtomicIntegerArray实例(此处可自定义初始化逻辑)
AtomicIntegerArray newArray = new AtomicIntegerArray(8);
// CAS原子操作:只有当当前引用为null时,才会更新为newArray
// 若更新成功,newArray即为初始化后的实例;若失败,说明其他线程已完成初始化
boolean initSuccess = ATOMIC_ARRAY_REF.compareAndSet(null, newArray);
if (initSuccess) {
System.out.println(Thread.currentThread().getName() + ":CAS初始化AtomicIntegerArray成功");
atomicArray = newArray;
} else {
System.out.println(Thread.currentThread().getName() + ":其他线程已完成初始化,直接获取");
atomicArray = ATOMIC_ARRAY_REF.get();
}
}
return atomicArray;
}
/
多线程高并发测试
/
public static void main(String[] args) throws InterruptedException {
// 启动30个线程,并发测试
for (int i = 0; i < 30; i++) {
new Thread(() -> {
AtomicIntegerArray array = getAtomicArray();
System.out.println(Thread.currentThread().getName() + ":获取到原子数组,长度:" + array.length());
}, "高并发线程-" + i).start();
}
Thread.sleep(1500);
System.out.println("初始化状态:" + (ATOMIC_ARRAY_REF.get() != null ? "已完成" : "未完成"));
}
}
运行结果(无锁安全,仅一次初始化成功)
高并发线程-0:CAS初始化AtomicIntegerArray成功
高并发线程-0:获取到原子数组,长度:8
高并发线程-1:其他线程已完成初始化,直接获取
高并发线程-1:获取到原子数组,长度:8
...(后续线程均直接获取已初始化的数组)
初始化状态:已完成
版权声明
本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。


