AtomicInteger位于java.util.concurrent.atomic包下沉颂,是對(duì)int的封裝条摸,提供原子性的訪問和更新操作,其原子性操作的實(shí)現(xiàn)是基于CAS铸屉。
1. CAS
CAS(compare-and-swap)直譯即比較并交換钉蒲,提供原子化的讀改寫能力,是Java 并發(fā)中所謂 lock-free 機(jī)制的基礎(chǔ)彻坛。
CAS的思想很簡單:三個(gè)參數(shù)顷啼,一個(gè)當(dāng)前內(nèi)存值V、舊的預(yù)期值A(chǔ)昌屉、即將更新的值B钙蒙,當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值修改為B并返回true怠益,否則什么都不做仪搔,并返回false。
可能會(huì)有面試官問 CAS 底層是如何實(shí)現(xiàn)的蜻牢,在JAVA中,CAS通過調(diào)用C++庫實(shí)現(xiàn)烤咧,由C++庫再去調(diào)用CPU指令集。不同體系結(jié)構(gòu)中抢呆,cpu指令還存在著明顯不同煮嫌。比如,x86 CPU 提供 cmpxchg 指令抱虐;而在精簡指令集的體系架構(gòu)中昌阿,(如“l(fā)oad and reserve”和“store conditional”)實(shí)現(xiàn)的,在大多數(shù)處理器上 CAS 都是個(gè)非常輕量級(jí)的操作恳邀,這也是其優(yōu)勢所在懦冰。
CAS的缺點(diǎn)有以下幾個(gè)方面:
- ABA問題
如果某個(gè)線程在CAS操作時(shí)發(fā)現(xiàn),內(nèi)存值和預(yù)期值都是A谣沸,就能確定期間沒有線程對(duì)值進(jìn)行修改嗎刷钢?答案未必,如果期間發(fā)生了 A -> B -> A 的更新乳附,僅僅判斷數(shù)值是 A内地,可能導(dǎo)致不合理的修改操作。針對(duì)這種情況赋除,Java 提供了 AtomicStampedReference 工具類阱缓,通過為引用建立類似版本號(hào)(stamp)的方式,來保證 CAS 的正確性举农。 - 循環(huán)時(shí)間長開銷大
CAS中使用的失敗重試機(jī)制荆针,隱藏著一個(gè)假設(shè),即競爭情況是短暫的。大多數(shù)應(yīng)用場景中航背,確實(shí)大部分重試只會(huì)發(fā)生一次就獲得了成功秸妥。但是總有意外情況,所以在有需要的時(shí)候沃粗,還是要考慮限制自旋的次數(shù),以免過度消耗 CPU键畴。
3.只能保證一個(gè)共享變量的原子操作
2.AtomicInteger原理淺析
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
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;
}
- 從 AtomicInteger 的內(nèi)部屬性可以看出最盅,它依賴于Unsafe 提供的一些底層能力,進(jìn)行底層操作起惕;如根據(jù)valueOffset代表的該變量值在內(nèi)存中的偏移地址涡贱,從而獲取數(shù)據(jù)的。
- 變量value用volatile修飾惹想,保證了多線程之間的內(nèi)存可見性问词。
下面以getAndIncrement為例,說明其原子操作過程
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
//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;
}
- 假設(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,而當(dāng)前的內(nèi)存值為2娃磺,CAS操作失敗薄湿,什么都不做,返回false
- 線程1重新通過getIntVolatile拿到最新的value為2偷卧,再進(jìn)行一次compareAndSwapInt操作豺瘤,這次操作成功,內(nèi)存值更新為3