JDK1.8源碼閱讀筆記(2) AtomicInteger AtomicLong AtomicBoolean原子類


JDK1.8源碼閱讀筆記(2) AtomicInteger AtomicLong AtomicBoolean原子類

Unsafe

Java中無法直接操作一塊內存區域,不能像C++中那樣可以自己申請內存和釋放內存。Java中的Unsafe類為我們提供了類似C++手動管理內存的能力。Unsafe類是"final"的,不允許繼承。

Number類(abstract)

xxxValue()方法:將 Number 對象轉換為xxx數據類型的值並返回。

Atomic Boolean-Integer-Long UML圖

AtomicInteger源碼

獲得value字段相對於AtomicInteger對象“起始地址”的偏移量:

  • Unsafe.getUnsafe();獲取unsafe對象;

  • public native long objectFieldOffset(Field var1);方法用於獲取某個字段相對Java對象的“起始地址”的偏移量

    • 一個java對象可以看成是一段內存,各個字段都得按照一定的順序放在這段內存里,同時考慮到對齊要求,可能這些字段不是連續放置的,用這個方法能准確地告訴你某個字段相對於對象的起始內存地址的字節偏移量,因為是相對偏移量,所以它其實跟某個具體對象又沒什么太大關系,跟class的定義和虛擬機的內存模型的實現細節更相關。
  • AtomicInteger.class.getDeclaredField("value")是拿到atomicInteger的value字段的field對象

  • valueoffset是拿到value的相對於AtomicInteger對象的地址偏移量valueOffset

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;

構造函數/get/set

 public AtomicInteger(int initialValue) {
     value = initialValue;
 }

public AtomicInteger() {
}

public final int get() {
        return value;
}

public final void set(int newValue) {
	value = newValue;
}

基於native方法的原子操作

getAnd×××方法,get在前就是先獲取值,然后操作,類似於i++;get在后則類似於i--,先操作,然后返回操作后的值。

AtomicInteger 的 getAndIncrement、getAndDecrement、getAndAdd、incrementAndGet、decrementAndGet、addAndGet 都是使用unsafe.getAndAddInt方法直接操作底層內存偏移地址對應的整型數值進行加減操作;getAndAccumulate、accumulateAndGet、updateAndGet方法都是通過輸入IntUnaryOperator接口類型的參數來實現邏輯的。

AtomicInteger中方法寫法比較類似,以updateAndGet為例說明,其他方法在下面有注釋

  • get()獲取當前值pre,將給定函數應用於當前值,原子更新結果next,返回更新后的值next
  • CAS操作:while判斷當前共享變量是否與自己獲取的prev值相等
    • 相等:設置當前共享變量值為next,跳出while循環,返回next值(getAndUpdate返回prev)
    • 不相等:說明有其他線程修改共享變量,while循環繼續運行
  • 即允許共享變量被多線程讀寫,while循環再次嘗試自旋機制(無鎖並發)
public final int updateAndGet(IntUnaryOperator updateFunction) {
	int prev, next;
    do {
        prev = get();
        next = updateFunction.applyAsInt(prev);
    } while (!compareAndSet(prev, next));
    return next;
}

set方法跟lazySet的區別

    public final void set(int newValue) {
        value = newValue;
    }
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

set方法跟lazySet的區別:

​ set方法的設置操作在寫操作的前后都加了內存屏障,因為AtomicInteger中的value是volatile修飾的。而lazySet方法並不是直接的操作value字段,而是通過Unsafe類的putOrderedInt方法先通過初始化時候計算出的vlaue字段的偏移變量找到字段地址,然后調用本地方法進行操作的,在本地方法中只在寫操作前面加了一個屏障,而后面沒有加。

其他方法:

    //cas替換,如果舊值與預期值相同,則swap
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    //與compareAndSet方法一樣,為什么要用這個方法
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
	//原子操作返回當前值,設置新值
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
	//value++,返回的是原來的值
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
	//value--,返回的是原來的值
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
	//返回value,然后value+=delta
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
	//++value,返回的是+1操作之后的value
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
	//--value
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }
	//value+=delta,然后返回value
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }
	//將給定函數應用於當前值,原子更新結果,返回以前的值。
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
	//將給定函數應用於當前值,原子更新結果,並返回以前的值
    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }
	//將給定函數應用於當前值和給定值,結果以原子更新當前值,並返回更改后的值
    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }

AtomicLong源碼與AtomicInteger源碼結構上幾乎相同,不再說明

AtomicBoolean

與AtomicInteger相同的部分

對比AtomicInteger,AtomicBoolean內部同樣包含一個value(int)屬性

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicBoolean.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;

構造函數

有參構造函數就是把傳進來的boolean轉化成了int

public AtomicBoolean(boolean initialValue) {
    value = initialValue ? 1 : 0;
}
public AtomicBoolean() {
}

其他原子操作方法

AtomicBoolean的其他方法本質上和AtomicInteger沒有什么區別,只是操作之前都要把傳進來的boolean類型的參數轉化為int。

public final boolean compareAndSet(boolean expect, boolean update) {
    int e = expect ? 1 : 0;
    int u = update ? 1 : 0;
    return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

public boolean weakCompareAndSet(boolean expect, boolean update) {
    int e = expect ? 1 : 0;
    int u = update ? 1 : 0;
    return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}

public final void set(boolean newValue) {
    value = newValue ? 1 : 0;
}

public final void lazySet(boolean newValue) {
    int v = newValue ? 1 : 0;
    unsafe.putOrderedInt(this, valueOffset, v);
}
public final boolean getAndSet(boolean newValue) {
    boolean prev;
    do {
        prev = get();
    } while (!compareAndSet(prev, newValue));
    return prev;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM