六、多線程-CAS

CAS

鎖的開銷極大。在某些場景,如保證一個變量的 read-modify-write操作的原子性。這種場景可以通過使用CAS解決而不需要用到鎖。

CAS,Compare and swap 比較並交換,是一種樂觀鎖的實現方式。是一箇中由處理器保證原子性的if-then-act操作。它通過提供一個變量內存位置,預期值(舊值)和新值。將預期值和變量的當前值進行比較,如果相等即證明變量並沒有被改變,將該變量修改成新值。如果不相等則進行重試(預期值會重新加載),直到成功。

Unsafe類中通過CAS修改int類型變量源碼。

/**
  *var1  AtomicInteger對象
  *var2  內存偏移量
  *var4  增加的值
  *var5  獲取的變量原值,保存在var5用於當預期值
  *compareAndSwapInt會比較內存值和var5相等的話就會改變內存值(即AtomicIntege * r對象中的變量)。
  */
  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;
  }

原子變量

以上代碼是原子遍歷AtomicInteger自增代碼的實現片段。JDK基於CAS提供了保證共享變量read-modify-write操作原子性的類。

分組 類名
基本類型 AtomicInteger,AtomicLong,AtomicBoolean
數組類型 AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
字段更新 AtomicIntegerFieldUpdater,AtomicLongFieldUpdater,AtomicReferenceFieldUpdater
引用型 AtomicReference,AtomicStampedReference,AtomicMarkableReference

AtomicInteger

方法 作用
int accumulateAndGet(int x, IntBinaryOperator accumulatorFunction) 使用將給定函數應用於當前值和給定值的結果原子更新當前值,返回更新後的值。
int addAndGet(int delta) 將給定的值原子地添加到當前值。
boolean compareAndSet(int expect, int update) 如果當前值 ==爲預期值,則將該值原子設置爲給定的更新值。
int decrementAndGet() 原子減1當前值。
double doubleValue() 返回此值 AtomicInteger爲 double一個寬元轉換後。
float floatValue() 返回此值 AtomicInteger爲 float一個寬元轉換後。
int get() 獲取當前值。
int getAndAccumulate(int x, IntBinaryOperator accumulatorFunction) 使用給定函數應用給當前值和給定值的結果原子更新當前值,返回上一個值。
int getAndAdd(int delta) 將給定的值原子地添加到當前值。
int getAndDecrement() 原子減1當前值。
int getAndIncrement() 原子上增加一個當前值。
int getAndSet(int newValue) 將原子設置爲給定值並返回舊值。
int getAndUpdate(IntUnaryOperator updateFunction) 用應用給定函數的結果原子更新當前值,返回上一個值。
int incrementAndGet() 原子上增加一個當前值。
int intValue() 將 AtomicInteger的值作爲 int 。
void lazySet(int newValue) 最終設定爲給定值。
long longValue() 返回此值 AtomicInteger爲 long一個寬元轉換後。
void set(int newValue) 設置爲給定值。
String toString() 返回當前值的String表示形式。
int updateAndGet(IntUnaryOperator updateFunction) 使用給定函數的結果原子更新當前值,返回更新的值。
boolean weakCompareAndSet(int expect, int update) 如果當前值 ==爲預期值,則將值設置爲給定更新值。

#### ABA問題 以上說到,CAS是將預期值和內存當前值比較,通過比較結果來判斷其他線程是否修改過該變量。但是如果存在其他線程修改變量後又改回原值(即預期值),在某些場景就會存在問題。

ABA問題例子

銀行賬戶 500元(共享變量)

  1. 要取出50元,機器故障發送了2個請求A,B,此時兩個請求的期望值都是500,新值450
  2. A請求執行完後,內存值變成450。所以第二個請求是不會成功的。
  3. 但是如果在B請求執行前,C又往賬戶存了50塊。這時銀行帳號變成500。B請求預期值滿足提交成功,銀行賬戶最終存款爲 450。

這種情況下,存款少了50塊。

ABA解決方案。

ABA問題可以通過版本號來解決,每次修改操作都添加一個版本號。例如剛纔的取款操作加個版本號 1,在存款操作執行後版本號+1,變爲2。取款的第二次請求執行時就會判斷版本號不是1,執行失敗。

ABA問題,原子變量AtomicStampedReference,AtomicMarkableReference用於解決ABA問題。

注意

  1. CAS只能保證一個共享變量的操作的原子性(原子性操作+原子性操作≠原子操作),如果要保持多個共享變量的操作的原子性,就必須使用鎖。
  2. 如果變量更新多次失敗,循環時間長開銷大。
  3. ABA問題
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章