Java源码

【Java源码计划】AtomicIntegerArray<

2019-04-03  本文已影响1人  DeanChangDM

AtomicIntegerArray

这个类是Atomic包下的类,用于提供对应类型的原子操作

跟其他的类一样,这个类提供了对整形数组的原子更新操作

首先是一些初始化相关内容


    //初始化Unsafe
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //记录数组的base地址
    private static final int base = unsafe.arrayBaseOffset(int[].class);
    //计算移位操作的数量的存储位置
    private static final int shift;
    //数组的实际承载
    private final int[] array;

    static {
        //读取数组的偏移量
        int scale = unsafe.arrayIndexScale(int[].class);
        //判断偏移位数是不是2的幂
        if ((scale & (scale - 1)) != 0)
            throw new Error("data type scale not a power of two");
        //计算并初始化移位操作的位数
        //numberOfLeadingZeros的作用是
        //返回无符号整型scale的最高非零位前面的0的个数,包括符号位在内
        //也就是说如果为负数,这个方法将会返回0,符号位为1
        //这个方法具体的放到对应的位置再说
        //整形32位,去掉0的位数后,也就是取得了移位操作需要的位数
        shift = 31 - Integer.numberOfLeadingZeros(scale);
    }
    
    //这个方法有两个作用,第一校验边界,然后将偏移量转换为byte
    
    public AtomicIntegerArray(int length) {
        array = new int[length];
    }
    private long checkedByteOffset(int i) {
        if (i < 0 || i >= array.length)
            throw new IndexOutOfBoundsException("index " + i);

        return byteOffset(i);
    }
    
    //方法用于转换偏移量,将整形的转换为byte的偏移量
    private static long byteOffset(int i) {
        return ((long) i << shift) + base;
    }
    
    //用给定的长度创建一个AtomicIntegerArray
    public AtomicIntegerArray(int length) {
        array = new int[length];
    }
    
    //用给定的数组初始化一个AtomicIntegerArray
    //长度跟给定的数组一样,并且所有元素都来自于这个数组
    public AtomicIntegerArray(int[] array) {
        // 注意这个字段的可见性是由final field的特性提供的
        // 因为这个字段只能初始化一次
        this.array = array.clone();
    }

下面是一些数组的常见方法和一些关键的读写操作

    //返回数组的长度
    public final int length() {
        return array.length;
    }
    
    //返回给定索引的元素
    public final int get(int i) {
        //注意结合上面的checkedByteOffset和下面的getRaw
        return getRaw(checkedByteOffset(i));
    }


    //根据给定的偏移量,读取数组元素
    private int getRaw(long offset) {
        return unsafe.getIntVolatile(array, offset);
    }

    //将给定的元素设置到给定的索引位置上
    public final void set(int i, int newValue) {
        //注意此处利用了checkedByteOffset将索引转换为了偏移量
        unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
    }
    
    //作用和上面的方法一样,区别在于只能保证最终会把指定索引的元素设置为给定元素
    public final void lazySet(int i, int newValue) {
        unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
    }
    
    //将给定变量原子性的设置到指定索引位置,然后返回原来的元素
    public final int getAndSet(int i, int newValue) {
        return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
    }
    
    //CAS操作,原子性的设置i的元素,当期望值和当前值相等,将update元素设置进去
    public final boolean compareAndSet(int i, int expect, int update) {
        return compareAndSetRaw(checkedByteOffset(i), expect, update);
    }
    
    //实际的CAS操作方法,将索引转换为了偏移量
    private boolean compareAndSetRaw(long offset, int expect, int update) {
        return unsafe.compareAndSwapInt(array, offset, expect, update);
    }
    
    //作用跟上面的一样
    //但是作为weak的方法,跟其他的weak方法一样可能出现不明确的failed,
    //所以通常不能作为compareAndSet的替代使用
    public final boolean weakCompareAndSet(int i, int expect, int update) {
        return compareAndSet(i, expect, update);
    }

下面是对getAndAdd方法的一些应用

    
    //原子性的实现给定索引位置的元素的自增
    public final int getAndIncrement(int i) {
        return getAndAdd(i, 1);
    }
    
    //同上,只是作用上是自减
    public final int getAndDecrement(int i) {
        return getAndAdd(i, -1);
    }
    
    //上面自增和自减的实际执行方法,原子性的对指定元素增加指定的大小,然后返回之前的元素
    public final int getAndAdd(int i, int delta) {
        return unsafe.getAndAddInt(array, checkedByteOffset(i), delta);
    }
    
    
    //本质上还是对getAnd的应用,作用是自增后返回更新的元素
    public final int incrementAndGet(int i) {
        return getAndAdd(i, 1) + 1;
    }
    
    
    //本质上是对getAndAdd应用,作用是自减后返回更新的元素
    public final int decrementAndGet(int i) {
        return getAndAdd(i, -1) - 1;
    }
    
    //给定索引增加delta,并返回更新后的元素
    public final int addAndGet(int i, int delta) {
        return getAndAdd(i, delta) + delta;
    }

下面是一些1.8之后新增的方法,主要和函数式变成有关

    
    //原子性的对给定未知的元素增加给定函数执行结果
    //注意,给定函数必须是无副作用的,因为在进行CAS操作时如果失败了
    //这个方法可能会再次执行
    //方法返回之前的元素
    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 updateAndGet(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 next;
    }
    
    //更加通用的方法,想必须前面两个方法,这个方法是把i对应位置上得知设置为
    //给定函数执行(当前元素为第一个参数,x为第二个参数)后的结果
    public final int accumulateAndGet(int i, int x,IntBinaryOperator accumulatorFunction) {
        long offset = checkedByteOffset(i);
        int prev, next;
        do {
            prev = getRaw(offset);
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSetRaw(offset, prev, next));
        return next;
    }
    
    

最后是比较常见的tostring方法


    public String toString() {
        //索引最大位置
        int iMax = array.length - 1;
        //最大索引位置不合法显示空的数组
        if (iMax == -1)
            return "[]";
        //迭代显示数组元素
        StringBuilder b = new StringBuilder();
        b.append('[');
        for (int i = 0; ; i++) {
            b.append(getRaw(byteOffset(i)));
            if (i == iMax)
                return b.append(']').toString();
            b.append(',').append(' ');
        }
    }
上一篇下一篇

猜你喜欢

热点阅读