1) 什么是CAS
- 應(yīng)用在并發(fā)編程領(lǐng)域
- CAS有3個操作數(shù):內(nèi)存值V寞埠,預(yù)期值A(chǔ)阔馋,要修改的值B,當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時钟哥,才將內(nèi)存值修改為B创肥,否則什么都不做达舒。最后返回現(xiàn)在的V作為新一輪預(yù)期值A(chǔ)值朋。
- 核心原理是
compare and swap
: 一個CPU操作指令,不可分割巩搏,具有原子性
2. 適用場景
- 樂觀鎖
- 并發(fā)容器
- 原子類
3) 以AtomicInteger為例昨登,分析在Java中是如何利用CAS實現(xiàn)原子操作的?
- 以AtomicInteger的getAndAdd方法為突破口贯底,分析該類是如何通過CAS實現(xiàn)并發(fā)下的累加操作丰辣。
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
- 這里用到了Unsafe類,Unsafe是CAS的核心類禽捆。Java無法直接訪問底層操作系統(tǒng)笙什,而是通過本地native方法類訪問。不過盡管如此胚想,JVM還是開了一個后門琐凭,JDK中有一個Unsafe,提供了硬件級別的原子操作浊服。
- AtomicInteger加載Unsafe工具统屈,用來直接操作內(nèi)存數(shù)據(jù)
- Unsafe來實現(xiàn)底層操作,Unsafe根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的原始值。
- 用volatile修飾value字段臼闻,保證可見性
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
// 類加載時鸿吆,獲取value的內(nèi)存地址偏移量 (相對對象地址)valueOffset
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
valueOffset表示的是變量值在內(nèi)存中相對對象偏移的地址,Unsafe根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的原值述呐,這樣我們就能通過unsafe來實現(xiàn)CAS惩淳。
- 接下來研究Unsafe的getAndAddInt方法的實現(xiàn)
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;
}
- 看著不直接,將變量替換以后
public final int getAndAddInt(Object atomicInteger, long offset, int addNumber) {
int expect;
do {
// 根據(jù)對象和偏移量獲取內(nèi)存值
expect = this.getIntVolatile(atomicInteger, offset);
// 執(zhí)行CAS算法,如果不成功乓搬,則自旋到成功為止
} while(!this.compareAndSwapInt(atomicInteger, offset, expect, expect + addNumber));
return expect;
}
- Unsafe類中的compareAndSwapInt的native方法部分實現(xiàn)
- 方法中先想辦法拿到變量value在內(nèi)存中的地址思犁。
- 通過Atomic::cmpxchg實現(xiàn)原子性的比較和替換,其中參數(shù)x是即將更新的值进肯,參數(shù)e是內(nèi)存的值激蹲。至此,最終完成了CAS的全過程江掩。
4) 缺點
-
ABA問題:由于直接對比的為操作值学辱,檢查的內(nèi)存值可能被修改過,只不過剛好與本次期望值相同(顯然环形,標(biāo)準(zhǔn)庫中CAS算法的設(shè)計認(rèn)為ABA問題不影響我期望的操作)
- 解決方式:添加版本號進(jìn)行比較(業(yè)務(wù)中在數(shù)據(jù)庫中進(jìn)行實現(xiàn))
自旋時間過長