AtomicIntegerArray 源码解析(基于 JDK 1.8)

23 篇文章 1 订阅


AtomicIntegerArray 可以原子的更新 int[] 中某个对象。

在找到数组第0个对象的偏移量之后,由于数组中每个对象是顺序排放的,可以根据对象大小计算出数组中某索引的偏移量,然后通过 Unsafe 相关的方法来获取或者修改。

1 偏移量的计算

假设数组中第 0 个对象的在数组中的偏移量为 x,每个对象的大小为 y,那么数组中第 i 个元素在数组中的偏移量为 x+i*y。下面是具体实现。

base 表示的就是第 0 个对象的偏移量 x,scale 或者 1<< shift 或者$2^{shift} 都表示每个对象的大小 y。

有几个点需要理解一下

  1. 如果 scale 是 2 的幂,则一定有 scale & (scale - 1)) == 0,这个是充要条件。

    证明:a 表示 scale是2的幂,b表示 scale & (scale - 1)) == 0。

    a -> b:

    假设scale是   ...00100...
    则scale-1是   ...00011...
    两个 & 得到   ...00000...
    

    b -> a:

    用逆否命题,非a -> 非b:
    如果scale不是2的幂,在大于0的情况下,
    可以找到最高位的1,由于不是2的幂,
    则最高位后面至少有一位是1
    即,   ...00100...1...
    -1 为  ...00100...0...
    两个 & 得到 非零
    
  2. $2^{shift} 就是 scale。

    说明:scale 此时为 2 的幂,只有一个1,从高到低为 a个0,1个1, b个0。

    其中 a+1+b =32。

    由于 numberOfLeadingZeros 返回 a,可知 shift 是 b,此时 1<<shift 正好表示 scale。

public class AtomicIntegerArray implements java.io.Serializable {
    private static final long serialVersionUID = 2862133569453604235L;

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //base 表示初始位置 x
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    //2^shift 等于 scale,表示每个对象的大小
    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 long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);

        return byteOffset(i);
    }

  	// 索引为 i 的偏移量
    private static long byteOffset(int i) {
      //return i*scale + base;
        return ((long) i << shift) + base;
    }

		
  ...
}

2 其他

首先介绍初始化,初始化有两种,一种是给定长度,生成新数组;另一种是给定数组,将数组设置为数组的深拷贝,防止不经过这个类修改而是直接修改外界数组,导致结果错误。

public AtomicIntegerArray(int length) {
        array = new int[length];
    }
    
    public AtomicIntegerArray(int[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = array.clone();
    }

其他的方法都使用 Unsafe,前面已经算出位置了,那就直接修改相应位置即可。有需要可以去看我的 Unsafe 讲解。

public final int get(int i) {
        return getRaw(checkedByteOffset(i));
    }
    
private int getRaw(long offset) {
        return unsafe.getIntVolatile(array, offset);
    }

public final void set(int i, int newValue) {
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }

public final int getAndUpdate(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 prev;
    }

public final int getAndSet(int i, int newValue) {
        return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
    }
public final void lazySet(int i, int newValue) {
        unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
    }
//其他方法省略

3 使用

import java.util.concurrent.atomic.AtomicIntegerArray;

public class A{

    public static void main(String[] args) throws Exception {

            AtomicIntegerArray array = new AtomicIntegerArray(new int[]{10,20,30,40,50});
            for (int i = 0; i <array.length() ; i++) {
                int j=i;
                new Thread(()->{
                    array.compareAndSet(j, 10, 30);
                    array.decrementAndGet(j);
                    array.getAndSet(j, array.get(j) - 1);
                }).start();

            }

            Thread.sleep(2000);
            System.out.println(array);

    }
}

下面是我的公众号,Java与大数据进阶,分享 Java 与大数据笔面试干货,欢迎关注
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值