AtomicInteger位于java.util.concurrent.atomic包下娩贷,是對int的封裝心肪,提供原子性的訪問和更新操作棵里,其原子性操作的實現(xiàn)是基于CAS。
包下還有其他的類似原子操作類冷尉,AtomicBoolean,AtomicLong等等系枪,大家可以自行查閱雀哨,實現(xiàn)的原理都是類似的
都是提供原子性的訪問和更新操作,其原子性操作的實現(xiàn)是基于CAS私爷。
1雾棺,什么CAS算法
- CAS(compare-and-swap)直譯即比較并交換,提供原子化的讀改寫能力衬浑,是Java 并發(fā)中所謂 lock-free 機制的基礎(chǔ)捌浩。
- CAS的思想很簡單:三個參數(shù),一個當前內(nèi)存值V工秩、舊的預(yù)期值A(chǔ)尸饺、即將更新的值B,當且僅當預(yù)期值A(chǔ)和內(nèi)存值V相同時拓诸,將內(nèi)存值修改為B并返回true侵佃,否則什么都不做麻昼,并返回false奠支。
- 可能會有面試官問 CAS 底層是如何實現(xiàn)的,在JAVA中,CAS通過調(diào)用C++庫實現(xiàn)抚芦,由C++庫再去調(diào)用CPU指令集倍谜。不同體系結(jié)構(gòu)中,cpu指令還存在著明顯不同叉抡。比如尔崔,x86 CPU 提供 cmpxchg 指令;而在精簡指令集的體系架構(gòu)中褥民,(如“l(fā)oad and reserve”和“store conditional”)實現(xiàn)的季春,在大多數(shù)處理器上 CAS 都是個非常輕量級的操作,這也是其優(yōu)勢所在消返。
2载弄,CAS的缺點
- ABA問題
如果某個線程在CAS操作時發(fā)現(xiàn),內(nèi)存值和預(yù)期值都是A撵颊,就能確定期間沒有線程對值進行修改嗎宇攻?答案未必,如果期間發(fā)生了 A -> B -> A 的更新倡勇,僅僅判斷數(shù)值是 A逞刷,可能導(dǎo)致不合理的修改操作。針對這種情況,Java 提供了 AtomicStampedReference 工具類夸浅,通過為引用建立類似版本號(stamp)的方式仑最,來保證 CAS 的正確性。
- 循環(huán)時間長開銷大
CAS中使用的失敗重試機制帆喇,隱藏著一個假設(shè)词身,即競爭情況是短暫的。大多數(shù)應(yīng)用場景中番枚,確實大部分重試只會發(fā)生一次就獲得了成功法严。但是總有意外情況,所以在有需要的時候葫笼,還是要考慮限制自旋的次數(shù)深啤,以免過度消耗 CPU。
- 只能保證一個共享變量的原子操作
3路星,AtomicInteger原理淺析
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long VALUE;
static {
try {
VALUE = U.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
private volatile int value;
}
從上面的代碼可以看出
- 從 AtomicInteger 的內(nèi)部屬性可以看出溯街,它依賴于Unsafe 提供的一些底層能力,進行底層操作洋丐;
- 如根據(jù)valueOffset代表的該變量值在內(nèi)存中的偏移地址呈昔,從而獲取數(shù)據(jù)的。
變量value用volatile修飾友绝,保證了多線程之間的內(nèi)存可見性堤尾。
下面以getAndIncrement為例,說明其原子操作過程
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
Unsafe的源碼
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;
}
- 假設(shè)線程1和線程2通過getIntVolatile拿到value的值都為1迁客,線程1被掛起郭宝,線程2繼續(xù)執(zhí)行
- 線程2在compareAndSwapInt操作中由于預(yù)期值和內(nèi)存值都為1,因此成功將內(nèi)存值更新為2
- 線程1繼續(xù)執(zhí)行掷漱,在compareAndSwapInt操作中粘室,預(yù)期值是1,而當前的內(nèi)存值為2卜范,CAS操作失敗衔统,什么都不做,返回false
- 線程1重新通過getIntVolatile拿到最新的value為2海雪,再進行一次compareAndSwapInt操作锦爵,這次操作成功,內(nèi)存值更新為3