Java 原子类 AtomicIntegerArray 数组原子操作

林欢喜 Java经验 发布时间:2025-12-25 15:30:05 阅读数:13783 1
下文笔者讲述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` 仅保证单个索引元素操作的原子性,
     若需要对多个索引元素进行原子性的批量操作,仍需手动加锁。
版权声明

本文仅代表作者观点,不代表本站立场。
本文系作者授权发表,未经许可,不得转载。

本文链接: https://www.Java265.com/JavaJingYan/202512/17666498028528.html

最近发表

热门文章

好文推荐

Java265.com

https://www.java265.com

站长统计|粤ICP备14097017号-3

Powered By Java265.com信息维护小组

使用手机扫描二维码

关注我们看更多资讯

java爱好者