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