Java多線程之JUC原子類 - 以原子方式操做數組AtomicLongArray

1、AtomicLongArray的介紹

    AtomicLongArray是JUC提供的以原子方式操做數組的一個類,存儲在AtomicLongArray中的數組元素可以以原子方式進行更新,它原子變量的實現依賴於sun.misc的Unsafe類提供的CAS操做和volatile的多線程內存可見性語義,下面咱們看下該類的數據結構。java

2、AtomicLongArray數據結構

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

}

 

3、AtomicLongArray源碼解析

1 - long checkAndByteOffSet(int i) 方法

    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。多線程

 

2 -  long get(int 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

 

3 - void set(int i, long value) - 以原子方式設置數組指定下標位置值

/**
     * 以原子方式設置數組指定下標位置值爲newValue
     */
    public final void set(int i, long newValue) {
        unsafe.putLongVolatile(array, checkedByteOffset(i), newValue);
    }

        set方法的基本邏輯與get相似,先校驗數組下標i,若校驗經過返回數組下標位置的地址,基於該地址和內部數組引用array設置新值newValue.索引

 

4 - long incrementAndGet方法 - 以原子方式在指定下標i元素加1,返回更新後的值

/**
     * 以原子方式在指定下標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

5 - void lazySet方法 - 延時更新數組下標元素,優先保證數據的修改操做,而下降對可見性的要求

/**
     * 更新數組指定下標i處的值爲newValue,與set的區別在於它是延時而不是當即更新,適用於對實時性要求不高的場景
     */
    public final void lazySet(int i, long newValue) {
        unsafe.putOrderedLong(array, checkedByteOffset(i), newValue);
    }

6 - 其餘方法

    其餘方法邏輯相似就不一一分析了

相關文章
相關標籤/搜索