JAVA CAS-ABA的問題解決 AtomicStampedReference

在說AtomicStampedReference前,我們回顧一下CAS是什麼?CAS 是Compare-And-Swap的簡寫即對比交換,它在保證數據原子性的前提下儘可能的減少了鎖的使用 也就是我們經常是的樂觀鎖 ;目標值 期望值。

Java中實現CAS主要還是依賴unsafe類提供的底層方法,看源碼我們可知其都是native 方法;所以說是原子性操作

public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);

public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);

public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);

CAS是如何導致ABA的尼,什麼是ABA?

線程1準備用CAS修改變量值A,在此之前,其它線程將變量的值由A替換爲B,又由B替換爲A,然後線程1執行CAS時發現變量的值仍然爲A,所以CAS成功。但實際上這時的現場已經和最初不同了。

 

public static AtomicInteger a = new AtomicInteger(1);
public static void main(String[] args){
    Thread main = new Thread(() -> {
        System.out.println("操作線程" + Thread.currentThread() +",初始值 = " + a);  //定義變量 a = 1
        try {
            Thread.sleep(1000);  //等待1秒 ,以便讓干擾線程執行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        boolean isCASSuccess = a.compareAndSet(1,2); // CAS操作
        System.out.println("操作線程" + Thread.currentThread() +",CAS操作結果: " + isCASSuccess);
    },"主操作線程");

    Thread other = new Thread(() -> {
        Thread.yield();  //確保thread-main線程優先執行
        a.incrementAndGet(); // a 加 1, a + 1 = 1 + 1 = 2
        System.out.println("操作線程" + Thread.currentThread() +",【increment】 ,值 = "+ a);
        a.decrementAndGet(); // a 減 1, a - 1 = 2 - 1 = 1
        System.out.println("操作線程" + Thread.currentThread() +",【decrement】 ,值 = "+ a);
    },"干擾線程");

    main.start();
    other.start();
}
// 輸出
> 操作線程Thread[主操作線程,5,main],初始值 = 1
> 操作線程Thread[干擾線程,5,main],【increment】 ,值 = 2
> 操作線程Thread[干擾線程,5,main],【decrement】 ,值 = 1
> 操作線程Thread[主操作線程,5,main],CAS操作結果: true

爲了解決Atomic可能出現的ABA問題 ,jdk提供了AtomicStampedReference類,很簡單 AtomicStampedReference 除了比較對象本身的 期望值,目標值之外,還增加了一個時間戳版本號,用於記錄對象的版本;對對象修改時,會同時比對對象和版本號。

下面是截取的源碼 其封裝了一個內部類 

  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);
        }
    }
  public boolean weakCompareAndSet(V   expectedReference,
                                     V   newReference,
                                     int expectedStamp,
                                     int newStamp) {
        return compareAndSet(expectedReference, newReference,
                             expectedStamp, newStamp);
    }


    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) ||
             casPair(current, Pair.of(newReference, newStamp)));
    }

casPair(current, Pair.of(newReference, newStamp)));方法就是直接調用unsafe的交換方法;和上面cas java實現一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章