1. 程式人生 > >Java CAS簡析

Java CAS簡析

tun 級別 integer 樂觀鎖 操作 func pri off title

什麽是CAS

CAS:Compare and Swap,它是一種原子操作,什麽是原子操作,可以在多線程編程中實現數據交換而不被打斷。是用來更新變量的,當多個線程使用CAS來更新變量時,只有一個線程可以更新變量的值,其他線程都會失敗,失敗的線程不回被掛起,而是重試直到成功為止。

CAS實現方式,有三個變量,內存值V,處理器緩存(預期值)A,更新值B。比較內存值與預期值,相等則說明沒有其他線程操作共享數據,則更新內存值為B;不等說明共享數據V已經被其他線程改動了,放棄更新操作,重新load共享數據,重試之前操作。

CAS的運用

樂觀鎖:其實現機制是基於CAS的,每次不加鎖,假設沒有沖突完成操作,如果有沖突,充實知道成功為止。

Java原子類:AtomicInteger等

源碼分析

一起看一下AtomicInteger是怎麽實現free-lock的。

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;
 public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

value 是用volatile 修飾的,可以保證get與set的原子性,但無法保證incrementAndGet讀寫改的復合操作。其實際調用的方法是unsafe的getAndAddInt

public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

unsafe 通過JNI調用C++ 的一些方法,首先是根據內存地址獲取到value的值,然後調用compareAndSwapInt,true:更新變量成功,false:不成功。compareAndSwapInt經過匯編後最主要的是CMPXCHG指令,該指令是 操作系統級別的原子操作。所以,拋開C++與匯編源碼,可以理解為Java運用了os的原子性來實現無鎖編程。

那操作系統是如何保證CAS原子性呢?在寫操作前會有一個Lock前綴,它會引發兩件事情。

  1. 將當前處理器緩存行的數據寫回到內存行。
  2. 這個寫緩存操作會使其他CPU裏的緩存了該內存地址的數據無效。
使用總線鎖保證原子性:

處理器提供一個Lock# 信號,當處理器在總線上輸出此信號時,其他處理的請求將被阻塞,改處理器就可以獨占共享內存。

缺點:對於多核(32,64),其中一個cpu鎖住了總線,其他所有核與內存之間的通信都被鎖住了,開銷太大。

使用緩存鎖保證原子性

直接修改內存,緩存一致性機制來保障操作的原子性。

ABA 問題

一個值原來是A,變成了B,又變成了A,那麽使用CAS進行檢查時會發現它的值沒有變化,其實卻變化了,解決思路是加上版本號。

參考:

  1. Java 並發編程的藝術
  2. 用AtomicStampedReference解決ABA問題
  3. 非阻塞同步算法與CAS(Compare and Swap)無鎖算法

Java CAS簡析