AtomicStampedReference源碼分析
問題
- AtomicStampedReference是什麼?
- AtomicStampedReference怎麼解決ABA問題?
參考
彤哥讀源碼
一:簡介
AtomicStampedReference是Java併發包下面提供的一個原子類,它能解決其他原子類無法解決的ABA問題。
ABA問題在前面的Unsafe類學習中有提到。它發生在多線程環境中,當某線程連續讀取同一塊內存地址兩次,兩次得到的值一樣,它簡單地認爲“此內存地址的值並沒有被修改過”,然而,同時可能存在另一個線程在着這兩次讀取之間把這個內存地址的值從A修改成了B又修改回了A,這時還簡單地認爲“沒有修改過”顯然是錯誤的。
彤哥在他的文章中舉了兩個很好的例子來表明ABA的問題,很形象。
二:源碼分析
屬性
// 內部定義類Pair<T>,兩個屬性,reference,stamp,將元素值和版本號綁定在一起
private static class Pair<T> {
final T reference;
final int stamp;
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
// 聲明一個Pair類型變量,在上面已經定義
private volatile Pair<V> pair;
// 獲取Unsafe實例
private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
// 獲取pair變量的偏移量,方便尋址
private static final long pairOffset =
objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);
方法
構造方法
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
構造方法只有一個,需要傳入兩個參數,一個是初始值,一個是初始版本號。
普通get/set方法
// 返回值
public V getReference() {
return pair.reference;
}
// 返回版本號
public int getStamp() {
return pair.stamp;
}
// 返回值,參數接收版本號
public V get(int[] stampHolder) {
Pair<V> pair = this.pair;
stampHolder[0] = pair.stamp;
return pair.reference;
}
public void set(V newReference, int newStamp) {
Pair<V> current = pair;
if (newReference != current.reference || newStamp != current.stamp)
this.pair = Pair.of(newReference, newStamp);
}
compareAndSet()方法
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
// 獲取當前pair
Pair<V> current = pair;
return
// 判斷當前reference有沒有變化
expectedReference == current.reference &&
// 判斷版本號有沒有變化
expectedStamp == current.stamp &&
// 判斷要更改的reference和版本號是否與當前的pair一致,是則不用更改,否則更新
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
private boolean casPair(Pair<V> cmp, Pair<V> val) {
return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}
調用了Unsafe類中的CAS方法,實現了原子操作,並通過版本號的引入克服了ABA問題。
三:總結
整個AtomicStampedReference類的源碼很簡潔,構建了內部類Pair來存儲元素值和版本號,通過版本號的引入來解決ABA問題。