AtomicStampedReference

AtomicStampedReference閱讀筆記

問題

1、ABA是什麼?

  • CAS情況下會導致這個發生。

    ABA問題發生在多線程環境中,當某線程連續讀取同一塊內存地址兩次,兩次得到的值一樣,它簡單地認爲“此內存地址的值並沒有被修改過”,然而,同時可能存在另一個線程在這兩次讀取之間把這個內存地址的值從A修改成了B又修改回了A,這時還簡單地認爲“沒有修改過”顯然是錯誤的。

2、ABA的危害?

  • 這篇文章的ABA危害寫的很好
  • 上面主要用一個棧來模擬,一個first線程cas操作時堵塞了,另外一個second線程去把棧進行B操作 ,再把原值繼續改回A,結果first線程cas判定成功

3、ABA的解決方案?

4、AtomicStampedReference是怎麼解決ABA的?

  • 使用版本號解決(自定義內部類Pair)

一、簡介

優點:

缺點:

二、繼承關係圖

三、存儲結構

自定義Pair內部類數據結構,存儲了元素值和版本號

四、源碼分析

內部類

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);
    }
}
//將元素值和版本號綁定在一起,存儲到reference 和stamp中

屬性

private volatile Pair<V> pair;
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);
}

主要方法:compareAndSet()方法

public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair<V> current = pair;
    return
        //預期的值和當前值是否一樣 &&
        expectedReference == current.reference &&
        //預期的版本號和當前的版本號一樣 &&
        expectedStamp == current.stamp &&
        
        (
        //新值和當前值一樣且新的版本和當前值一樣,則不做變化,返回即可
        (newReference == current.reference &&
          newStamp == current.stamp) ||
        //cas替換,Unsafe.compareAndSwapObject,比較和替換對象
         casPair(current, Pair.of(newReference, newStamp))
    );
}

使用方式

public static void main(String[] args) throws InterruptedException {
    AtomicStampedReference<String> atomic = new AtomicStampedReference<>("aaa",1);
    //獲取元素值(pair.reference)
    System.out.println(atomic.getReference());//"aaa"
    //獲取版本號(pair.stamp)
    System.out.println(atomic.getStamp());//1
    //不修改元素值,只修改版本,
    boolean isOk = atomic.attemptStamp("aaa",atomic.getStamp() + 1);
    System.out.println(isOk);//true
    System.out.println(atomic.getStamp());//2
    //修改元素值和版本號
    boolean isOk1 = atomic.compareAndSet(atomic.getReference(),"bbb",atomic.getStamp(),atomic.getStamp() + 1);
    System.out.println(isOk1);//true
    System.out.println(atomic.getReference());//"bbb"
    System.out.println(atomic.getStamp());//3
    //與compareAndSet一樣,因爲內部直接調用的compareAndSet方法
    boolean isOk2 = atomic.weakCompareAndSet(atomic.getReference(),"ccc",atomic.getStamp(),atomic.getStamp() + 1);
    System.out.println(isOk2);//true
    System.out.println(atomic.getReference());//"ccc"
    System.out.println(atomic.getStamp());//4
    //獲得版本號和元素值
    int [] stamp = new int[1];
    String reFenence = atomic.get(stamp);
    System.out.println("stamp:"+stamp[0]);//stamp:4
    System.out.println("reFenence:"+reFenence);//reFenence:ccc
}

五、總結

1、在多線程下使用無鎖結構需要注意ABA問題

2、ABA的解決一般使用版本號來控制,並且保證數據結構使用元素值來傳遞,且每次添加元素都新建節點承載元素值。

3、AtomicStampedReference採用內部Pair類來存儲元素值和版本號。

額外:

AtomicMarkableReference 也可以解決ABA問題,他不維護版本號,使用的是一個boolean類型的標記

六、參考

彤的公衆號

發佈了48 篇原創文章 · 獲贊 17 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章