AtomicIntegerArray源码解析

时间:2022-07-23
本文章向大家介绍AtomicIntegerArray源码解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

原理概述

AtomicInteger原子类是保障Integer的高并发的原子性的,那么AtomicAIntegerArray就是Integer类型数组的高并发原子性质的数组。那么如何做到数组的原子性的?因为AtomicInteger是采用的线程可见的volatile,那么采用volatile来修饰数组是否可行?显然不能这么干,我们在操作数据的时候一定是作用于数据元上,而不能作用整个数组中,当然作用于整个数组时,我们也无法保障当前线程获取到数组并将下标移动到目标地址的时候,其他的线程是否也同样移动到了目标地址。再操作的瞬间是否有其他的线程已经修改值,那么最终的解决方案就是要不断的获取整个数组并移动到目标地址,再使用CAS进行不断判断,如果数组特别长,那么volatile关键字的意义已经不那么大了。其损耗效率会比CAS自旋的还大。那么如何让减少这种无效的损耗?可以减少数组下标移动的消耗,也就是将volatile的可见性定义到数组的元素中去,而不是数组上。我们在多数组元素进行操作之前,首先将地址移动到目标数组的地址上,然后通过CAS去轮询而不是每次都从头开始。AtomicIntegerArray就是采用的这种策略。

实现方式

由于java中的对象都是存放于堆中,而堆是线程公用的空间。而多线程的物理意义就是将计算机指令的复制,并用复制的代码的多核运行。对于数组的原子性,AtomicIntegerArray首先在数组初始化的时候将数组的地址和数组元素的空间位数大小记录下来。当需要对数据进行操作的时候,通过基地址和元素空间位数大小以及数组下标便可以计算出其地址空间,进而获取数据或者对其进行赋值。由于AtomicIntegerArray中并没有采用AtomicInteger,而是采用了基础数据类型Int,可能也是避免基础类型和对象的混用吧。

AtomicIntegerArray的主要接口


   //进行CAS运算,判断第i个元素的expect是否与新的expect相同
    public final boolean compareAndSet(int i, int expect, 
                 int update);

    //第i个元素的加一操作并返回
    public final int incrementAndGet(int i);

    //原子性的i个元素的减一操作
    public final int decrementAndGet(int i);

    //第i个元素与delta进行加法操作,复制之后返回计算结果
    public final int addAndGet(int i, int delta);


   //获取值并进行复杂的计算,并返回原值
    public final int getAndUpdate(int i, 
                 IntUnaryOperator updateFunction);

    //在指定的下标处进行复杂运算更新
    public final int updateAndGet(int i, 
                  IntUnaryOperator updateFunction);

    //获取指定下标的值,并与X进行复杂的数值计算之后赋值
    public final int getAndAccumulate(int i, int x,
                   IntBinaryOperator accumulatorFunction);

    //操作下标为i的元素和x进行复杂的数据计算赋值之后返回
    public final int accumulateAndGet(int i, int x,
                    IntBinaryOperator accumulatorFunction);