美文网首页Java源码
【Java源码计划】AtomicIntegerArray<

【Java源码计划】AtomicIntegerArray<

作者: DeanChangDM | 来源:发表于2019-04-03 18:11 被阅读1次

    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(' ');
            }
        }
    

    相关文章

      网友评论

        本文标题:【Java源码计划】AtomicIntegerArray<

        本文链接:https://www.haomeiwen.com/subject/bjttiqtx.html