AtomicLongArray是JUC提供的以原子方式操做数组的一个类,存储在AtomicLongArray中的数组元素可以以原子方式进行更新,它原子变量的实现依赖于sun.misc的Unsafe类提供的CAS操做和volatile的多线程内存可见性语义,下面咱们看下该类的数据结构。java
public class AtomicLongArray implements java.io.Serializable { private static final long serialVersionUID = -2308431214976778248L; // unsafe变量是AutomicLongArray实现数组原子化更新的核心,数组元素的修改操做由unsafe的CAS相关操做完成 private static final Unsafe unsafe = Unsafe.getUnsafe(); // base是数组首个元素偏移地址 private static final int base = unsafe.arrayBaseOffset(long[].class); // 数组元素的偏移量 private static final int shift; // 保存元素的数组 private final long[] array; static { // 获取数组元素的增量偏移 int scale = unsafe.arrayIndexScale(long[].class); // 判断是否是2的倍数 if ((scale & (scale - 1)) != 0) throw new Error("data type scale not a power of two"); // 获取long型元素的偏移量 shift = 31 - Integer.numberOfLeadingZeros(scale); } }
checkAndByteOffset(int i)方法主要用于判断索引值是否越界,若越界则抛出越界异常,不然计算索引值对应的元素在数组中的内存偏移量数组
private long checkedByteOffset(int i) { if (i < 0 || i >= array.length) throw new IndexOutOfBoundsException("index " + i); return byteOffset(i); }
方法首先校验了索引的范围,若索引i越界则抛出IndexOutOfBoundsException异常,不然调用byteOffSet方法,咱们进入byteOffset方法源码内部看下方法内部作了什么数据结构
private static long byteOffset(int i) { return ((long) i << shift) + base; }
方法很简单,根据索引下标i计算出每一个元素的内存地址,元素内存地址=首地址偏移+每一个元素的相对于数组偏移,每一个数组元素相对数组的偏移量等于元素所占内存8*数组下标i。多线程
public final long get(int i) { return getRaw(checkedByteOffset(i)); }
方法逻辑不难,首先调用checkedByteOffset方法校验数组的索引下标i,校验经过返回对应下标元素的内存地址,接着将该地址做为参数调用getRow方法,进入该方法源码:spa
private long getRaw(long offset) { return unsafe.getLongVolatile(array, offset); }
该方法在内部调用了unsafe.getLongVolitile方法获取数组指定下标i的元素。线程
long get(int i)方法的基本逻辑总结以下:1)校验数组下标i,若越界抛出异常,不然获取索引下标元素的内存地址;2)基于元素的内存地址获取指定下标的数组元素。code
/** * 以原子方式设置数组指定下标位置值为newValue */ public final void set(int i, long newValue) { unsafe.putLongVolatile(array, checkedByteOffset(i), newValue); }
set方法的基本逻辑与get相似,先校验数组下标i,若校验经过返回数组下标位置的地址,基于该地址和内部数组引用array设置新值newValue.索引
/** * 以原子方式在指定下标i元素加1,返回更新后的值 */ public final long incrementAndGet(int i) { return getAndAdd(i, 1) + 1; }
看下getAndAdd方法源码内存
public final long getAndAdd(int i, long delta) { return unsafe.getAndAddLong(array, checkedByteOffset(i), delta); }
到这里就清楚了,该方法首先对数组索引下标i作了范围校验,校验经过获取数组下标位置的地址,基于该地址调用unsafe的getAndAddLong对该位置元素使用CAS操做更新,注意此时返回的long值时更新前的因此咱们在调用处incrementAndGet方法还要加1。rem
/** * 更新数组指定下标i处的值为newValue,与set的区别在于它是延时而不是当即更新,适用于对实时性要求不高的场景 */ public final void lazySet(int i, long newValue) { unsafe.putOrderedLong(array, checkedByteOffset(i), newValue); }
其余方法逻辑相似就不一一分析了