juc-atomic原子类之三: AtomicLongArray,AtomicLongArray,AtomicReferenceArray原子类

java.util.concurrent.atomic.AtomicXXXArray包括三种具体类:AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray

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

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

如果把数组定义为volatile类型,那么其里面数组元素在读写方面是没有volatile语义。

直接看代码中定义:jdk8

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方法: 

    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语义的方法,也就是整个通过内存地址对数组元素的操作,也是有volatile语义的,即具有可见性

AtomicLongArray源码

// 创建给定长度的新 AtomicLongArray。
AtomicLongArray(int length)
// 创建与给定数组具有相同长度的新 AtomicLongArray,并从给定数组复制其所有元素。
AtomicLongArray(long[] array)

// 以原子方式将给定值添加到索引 i 的元素。
long addAndGet(int i, long delta)
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
boolean compareAndSet(int i, long expect, long update)
// 以原子方式将索引 i 的元素减1。
long decrementAndGet(int i)
// 获取位置 i 的当前值。
long get(int i)
// 以原子方式将给定值与索引 i 的元素相加。
long getAndAdd(int i, long delta)
// 以原子方式将索引 i 的元素减 1。
long getAndDecrement(int i)
// 以原子方式将索引 i 的元素加 1。
long getAndIncrement(int i)
// 以原子方式将位置 i 的元素设置为给定值,并返回旧值。
long getAndSet(int i, long newValue)
// 以原子方式将索引 i 的元素加1。
long incrementAndGet(int i)
// 最终将位置 i 的元素设置为给定值。
void lazySet(int i, long newValue)
// 返回该数组的长度。
int length()
// 将位置 i 的元素设置为给定值。
void set(int i, long newValue)
// 返回数组当前值的字符串表示形式。
String toString()
// 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。
boolean    weakCompareAndSet(int i, long expect, long update)

AtomicLongArray的代码很简单,下面仅以incrementAndGet()为例,对AtomicLong的原理进行说明。
incrementAndGet()源码如下:

public final long incrementAndGet(int i) {
    return addAndGet(i, 1);
}

说明:incrementAndGet()的作用是以原子方式将long数组的索引 i 的元素加1,并返回加1之后的值。

 

addAndGet()源码如下:

复制代码
public long addAndGet(int i, long delta) {
    // 检查数组是否越界
    long offset = checkedByteOffset(i);
    while (true) {
        // 获取long型数组的索引 offset 的原始值
        long current = getRaw(offset);
        // 修改long型值
        long next = current + delta;
        // 通过CAS更新long型数组的索引 offset的值。
        if (compareAndSetRaw(offset, current, next))
            return next;
    }
}
复制代码

说明:addAndGet()首先检查数组是否越界。如果没有越界的话,则先获取数组索引i的值;然后通过CAS函数更新i的值。

 

getRaw()源码如下:

private long getRaw(long offset) {
    return unsafe.getLongVolatile(array, offset);
}

说明:unsafe是通过Unsafe.getUnsafe()返回的一个Unsafe对象。通过Unsafe的CAS函数对long型数组的元素进行原子操作。如compareAndSetRaw()就是调用Unsafe的CAS函数,它的源码如下:

private boolean compareAndSetRaw(long offset, long expect, long update) {
    return unsafe.compareAndSwapLong(array, offset, expect, update);
}

 

AtomicLongArray示例

复制代码
 1 // LongArrayTest.java的源码
 2 import java.util.concurrent.atomic.AtomicLongArray;
 3 
 4 public class LongArrayTest {
 5     
 6     public static void main(String[] args){
 7 
 8         // 新建AtomicLongArray对象
 9         long[] arrLong = new long[] {10, 20, 30, 40, 50};
10         AtomicLongArray ala = new AtomicLongArray(arrLong);
11 
12         ala.set(0, 100);
13         for (int i=0, len=ala.length(); i<len; i++) 
14             System.out.printf("get(%d) : %s\n", i, ala.get(i));
15 
16         System.out.printf("%20s : %s\n", "getAndDecrement(0)", ala.getAndDecrement(0));
17         System.out.printf("%20s : %s\n", "decrementAndGet(1)", ala.decrementAndGet(1));
18         System.out.printf("%20s : %s\n", "getAndIncrement(2)", ala.getAndIncrement(2));
19         System.out.printf("%20s : %s\n", "incrementAndGet(3)", ala.incrementAndGet(3));
20 
21         System.out.printf("%20s : %s\n", "addAndGet(100)", ala.addAndGet(0, 100));
22         System.out.printf("%20s : %s\n", "getAndAdd(100)", ala.getAndAdd(1, 100));
23 
24         System.out.printf("%20s : %s\n", "compareAndSet()", ala.compareAndSet(2, 31, 1000));
25         System.out.printf("%20s : %s\n", "get(2)", ala.get(2));
26     }
27 }
复制代码

运行结果

复制代码
get(0) : 100
get(1) : 20
get(2) : 30
get(3) : 40
get(4) : 50
  getAndDecrement(0) : 100
  decrementAndGet(1) : 19
  getAndIncrement(2) : 30
  incrementAndGet(3) : 41
      addAndGet(100) : 199
      getAndAdd(100) : 19
     compareAndSet() : true
              get(2) : 1000

转自:http://www.cnblogs.com/skywang12345/p/3514604.html

 

posted on 2013-12-09 11:30  duanxz  阅读(630)  评论(0编辑  收藏  举报