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); }
其餘方法邏輯相似就不一一分析了