淺談AtomicInteger實(shí)現(xiàn)原理

AtomicInteger位于java.util.concurrent.atomic包下,是對int的封裝,提供原子性的訪問和更新操作,其原子性操作的實(shí)現(xiàn)是基于CAS。

1. CAS

CAS(compare-and-swap)直譯即比較并交換,提供原子化的讀改寫能力,是Java 并發(fā)中所謂 lock-free 機(jī)制的基礎(chǔ)。
CAS的思想很簡單:三個參數(shù),一個當(dāng)前內(nèi)存值V、舊的預(yù)期值A(chǔ)、即將更新的值B,當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時,將內(nèi)存值修改為B并返回true,否則什么都不做,并返回false。
可能會有面試官問 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 都是個非常輕量級的操作,這也是其優(yōu)勢所在。

CAS的缺點(diǎn)有以下幾個方面:

  1. ABA問題
    如果某個線程在CAS操作時發(fā)現(xiàn),內(nèi)存值和預(yù)期值都是A,就能確定期間沒有線程對值進(jìn)行修改嗎?答案未必,如果期間發(fā)生了 A -> B -> A 的更新,僅僅判斷數(shù)值是 A,可能導(dǎo)致不合理的修改操作。針對這種情況,Java 提供了 AtomicStampedReference 工具類,通過為引用建立類似版本號(stamp)的方式,來保證 CAS 的正確性。
  2. 循環(huán)時間長開銷大
    CAS中使用的失敗重試機(jī)制,隱藏著一個假設(shè),即競爭情況是短暫的。大多數(shù)應(yīng)用場景中,確實(shí)大部分重試只會發(fā)生一次就獲得了成功。但是總有意外情況,所以在有需要的時候,還是要考慮限制自旋的次數(shù),以免過度消耗 CPU。
    3.只能保證一個共享變量的原子操作

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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容