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元(共享變量)
- 要取出50元,機器故障發送了2個請求A,B,此時兩個請求的期望值都是500,新值450
- A請求執行完後,內存值變成450。所以第二個請求是不會成功的。
- 但是如果在B請求執行前,C又往賬戶存了50塊。這時銀行帳號變成500。B請求預期值滿足提交成功,銀行賬戶最終存款爲 450。
這種情況下,存款少了50塊。
ABA解決方案。
ABA問題可以通過版本號來解決,每次修改操作都添加一個版本號。例如剛纔的取款操作加個版本號 1,在存款操作執行後版本號+1,變爲2。取款的第二次請求執行時就會判斷版本號不是1,執行失敗。
ABA問題,原子變量AtomicStampedReference,AtomicMarkableReference用於解決ABA問題。
注意
- CAS只能保證一個共享變量的操作的原子性(原子性操作+原子性操作≠原子操作),如果要保持多個共享變量的操作的原子性,就必須使用鎖。
- 如果變量更新多次失敗,循環時間長開銷大。
- ABA問題