zoukankan      html  css  js  c++  java
  • AtomicIntegerArray数组类型类

     

    前一篇文章学习了AtomicXXX基本数据类型类,可以为int,boolean或者reference类型,也就是单个元素的原子类。那么数组类型呢? 
    下面以AtomicIntegerArray为例进行分析。 
    AtomicXXXArray包括三种具体类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray

    What is AtomicIntegerArray

    具体的介绍,都已经在开头讲过了,AtomicIntegerArray有以下特点:

    • 可以存放int数值的原子性数组
    • 以整个数组对象为单位,里面的元素操作都是原子性的

    实现

    从以前的文章分析可以了解,一般原子类的实现都是volatile+CAS的方式,那么AtomicIntegerArray也是么? 
    首先,作为原子性数组,里面肯定有个int类型数组,那数组是volatile类型么? 
    前面文章分析可以知道,如果把数组定义为volatile类型,那么其里面数组元素在读写方面是没有volatile语义。 可以参看:Java并发学习(二)-JMM 。

    直接看代码中定义:

    private final int[] array;

    定义了一个final的int类型数组,final的内存语义则是使用时,一定是已经直接初始化或者通过构造方法初始化好的。

    //获取int[]在内存中的初始地址。
     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);
            if ((scale & (scale - 1)) != 0)
                throw new Error("data type scale not a power of two");
            //得出scale为2的几次方,即需要移位个数
            shift = 31 - Integer.numberOfLeadingZeros(scale);
        }
        //检查第i个元素的地址值。
        private long checkedByteOffset(int i) {
            if (i < 0 || i >= array.length)
                throw new IndexOutOfBoundsException("index " + i);
            return byteOffset(i);
        }
        //当前索引i*shift(偏移位置) + base(基础位置)
        private static long byteOffset(int i) {
            return ((long) i << shift) + base;
        }
         //获取第i个元素的值
        public final int get(int i) {
            return getRaw(checkedByteOffset(i));
        }
        //通过地址值来获取偏移量的元素值。
        private int getRaw(long offset) {
            return unsafe.getIntVolatile(array, offset);
        }
        //用cas方式,在元素i的位置设置新值
        public final void set(int i, int newValue) {
            unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
        }
    

    核心代码可以看上面,具体都有注释,可能有点模糊,这里再说说核心思想: 
    我们知道,数组在内存中是连续存储的,如下: 
    这里写图片描述

    并且数组里面各个元素类别都是相同的,所以占有的空间也都是一样大的,假设上面数组为int类型的array,并且array的地址为n,所以可以计算出array[1]为array+4,array[2]为 
    array+4*2,array[3]为array+4*3 。 
    所以这样在AtomicIntegerArray里面,我们可以通过base,i,scale和shift,能够计算出数组中任意元素的位置以及获取值,这样一来,对数组的操作就可以转化为对单个元素的操作。 
    开始被一个问题困扰了一会儿,array数组是final类型,保证了:

    • array在使用的时候,已经初始化了
    • array不能再重新指向其他对象

    但是,array数组里面并不是volatile类型的,能确保可见性么?

    我们再来看看它的get方法和set方法: 
    get方法:

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

    set方法:

        //volatile的set
        public final void set(int i, int newValue) {
            unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
        }
    
        //lazySet,即普通set,性能高
        public final void lazySet(int i, int newValue) {
            unsafe.putOrderedInt(array, checkedByteOffset(i), newValue);
        }
    
        //原子性的获取并且set
        public int getAndSet(int i, int newValue) {
            return unsafe.getAndSetInt(array, checkedByteOffset(i), newValue);
        }

    如上我们可以看到,调用的都是unsafe里面具有volatile语义的方法,也就是整个通过内存xia地址对数组元素的操作,也是有volatile语义的,即具有可见性。

    AtomicLongArray和AtomicObjectArray

    这两者与AtomicIntegerArray实现基本一致,只是数组对象分别为long[]和Object[]类型。

  • 相关阅读:
    利用 .NET Framework 命令行工具
    和AI机器人Alice的一段聊天记录
    WINDOWS自启动程序的10大隐身之所
    开放式开发/开源项目TimeDog[C#WindowsApp]
    Microsoft月度中文速递
    新安装的VS2003出现了一个问题不能新建项目!
    实例构造器和类型构造器的一些比较
    C++ 模板里面的typename
    vim 折叠的用法
    Redis3:持久化
  • 原文地址:https://www.cnblogs.com/zyy1688/p/10654943.html
Copyright © 2011-2022 走看看