深入解析Java AtomicInteger 原子類型

Java開發(fā)中不可避免的會遇到并發(fā)的問題。在進(jìn)行并發(fā)編程的時候我們需要確保程序在被多個線程并發(fā)訪問時可以得到正確的結(jié)果,也就是要實現(xiàn)線程安全。
那么什么樣的標(biāo)準(zhǔn)可以稱為線程安全呢?這里有線程安全的定義:

當(dāng)多個線程訪問某個類時,不管運行時環(huán)境采用何種調(diào)度方式或者這些線程將如何交替執(zhí)行,并且在主調(diào)代碼中不需要任何額外的同步或協(xié)同,這個類都能表現(xiàn)出正確的行為,那么這個類就是線程安全的。

舉一個線程不安全的小例子。假如我們想實現(xiàn)一個功能來統(tǒng)計網(wǎng)頁訪問量,首先我們可能想到用count++ 的方法來統(tǒng)計訪問量。count++ 其實可以分成三個獨立的操作:

  1. 獲取變量當(dāng)前值
  2. 給獲取的當(dāng)前變量值+1
  3. 寫回新的值到變量

假設(shè)count的初始值為10,當(dāng)進(jìn)行并發(fā)操作的時候,可能出現(xiàn)線程A和線程B都進(jìn)行到了1操作,之后又同時進(jìn)行2操作。A先進(jìn)行到3操作+1,現(xiàn)在值為11;注意剛才AB獲取到的當(dāng)前值都是10,所以B執(zhí)行3操作后,count的值依然是11。這個結(jié)果顯然不符合我們的要求。因此這個count++操作不是線程安全的。

實現(xiàn)線程安全的目標(biāo),我們需要引入本篇的主角—— AtomicInteger 。本篇我們介紹AtomicInteger原子類型內(nèi)部是如何實現(xiàn)線程安全的。

不多說,先看看AtomicInteger 的源碼:

package java.util.concurrent.atomic;
import sun.misc.Unsafe;

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;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
  
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public String toString() {
        return Integer.toString(get());
    }


    public int intValue() {
    return get();
    }

    public long longValue() {
    return (long)get();
    }

    public float floatValue() {
    return (float)get();
    }

    public double doubleValue() {
    return (double)get();
    }

}

一、AtomicInteger中定義的屬性

   // 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); }
    }

第一個變量是Unsafe,Unsafe是JDK內(nèi)部的工具類,主要實現(xiàn)了平臺相關(guān)的操作。下面內(nèi)容引自JDK官方文檔:

sun.misc.Unsafe是JDK內(nèi)部用的工具類。它通過暴露一些Java意義上說“不安全”的功能給Java層代碼,來讓JDK能夠更多的使用Java代碼來實現(xiàn)一些原本是平臺相關(guān)的、需要使用native語言(例如C或C++)才可以實現(xiàn)的功能。該類不應(yīng)該在JDK核心類庫之外使用。
Unsafe的具體實現(xiàn)跟本篇的目標(biāo)關(guān)聯(lián)不大,你只要知道這段代碼是為了獲取value在堆內(nèi)存中的偏移量就夠了。
第二個變量是valueOffset,也就是內(nèi)存偏移量。偏移量在AtomicInteger中很重要,AtomicInteger的原子操作都靠內(nèi)存偏移量來實現(xiàn)的。

二、Value的定義和volatile

AtomicInteger 本身是個整型,所以最重要的屬性就是value,我們看看它是如何聲明value的

 private volatile int value;

我們看到value使用了volatile修飾符,那么什么是volatile呢?

volatile相當(dāng)于synchronized的弱實現(xiàn),也就是說volatile實現(xiàn)了類似synchronized的語義,卻又沒有鎖機(jī)制。它確保對volatile字段的更新以可預(yù)見的方式告知其他的線程。

volatile包含以下語義:

  1. Java 存儲模型不會對valatile指令的操作進(jìn)行重排序:這個保證對volatile變量的操作時按照指令的出現(xiàn)順序執(zhí)行的。
  2. volatile變量不會被緩存在寄存器中(只有擁有線程可見)或者其他對CPU不可見的地方,每次總是從主存中讀取volatile變量的結(jié)果。也就是說對于volatile變量的修改,其它線程總是可見的,并且不是使用自己線程棧內(nèi)部的變量。也就是在happens-before法則中,對一個valatile變量的寫操作后,其后的任何讀操作理解可見此寫操作的結(jié)果。

簡而言之volatile 的作用是當(dāng)一個線程修改了共享變量時,另一個線程可以讀取到這個修改后的值。在分析AtomicInteger 源碼時,我們了解到這里就足夠了。

三、用CAS操作實現(xiàn)安全的自增

AtomicInteger中有很多方法,例如incrementAndGet() 相當(dāng)于i++getAndAdd() 相當(dāng)于i+=n 。從源碼中我們可以看出這幾種方法的實現(xiàn)很相似,所以我們主要分析incrementAndGet() 方法的源碼。

源碼如下:

 public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

 public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

incrementAndGet() 方法實現(xiàn)了自增的操作。核心實現(xiàn)是先獲取當(dāng)前值和目標(biāo)值(也就是value+1),如果compareAndSet(current, next) 返回成功則該方法返回目標(biāo)值。那么compareAndSet是做什么的呢?理解這個方法我們需要引入CAS操作。

在大學(xué)操作系統(tǒng)課程中我們學(xué)過獨占鎖和樂觀鎖的概念。獨占鎖就是線程獲取鎖后其他的線程都需要掛起,直到持有獨占鎖的線程釋放鎖;樂觀鎖是先假定沒有沖突直接進(jìn)行操作,如果因為有沖突而失敗就重試,直到操作成功。其中樂觀鎖用到的機(jī)制就是CAS,Compare and Swap。

AtomicInteger 中的CAS操作就是compareAndSet(),其作用是每次從內(nèi)存中根據(jù)內(nèi)存偏移量(valueOffset)取出數(shù)據(jù),將取出的值跟expect 比較,如果數(shù)據(jù)一致就把內(nèi)存中的值改為update。

這樣使用CAS就保證了原子操作。其余幾個方法的原理跟這個相同,在此不再過多的解釋。

沒看AtomicInteger 源碼之前,我認(rèn)為其內(nèi)部是用synchronized 來實現(xiàn)的原子操作。查閱資料后發(fā)現(xiàn)synchronized 會影響性能,因為Java中的synchronized 鎖是獨占鎖,雖然可以實現(xiàn)原子操作,但是這種實現(xiàn)方式的并發(fā)性能很差。

四、總結(jié)

總結(jié)一下,AtomicInteger 中主要實現(xiàn)了整型的原子操作,防止并發(fā)情況下出現(xiàn)異常結(jié)果,其內(nèi)部主要依靠JDK 中的unsafe 類操作內(nèi)存中的數(shù)據(jù)來實現(xiàn)的。volatile 修飾符保證了value在內(nèi)存中其他線程可以看到其值得改變。CAS操作保證了AtomicInteger 可以安全的修改value 的值。

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

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

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,896評論 0 11
  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍(lán)閱讀 42,793評論 11 349
  • 原來一直不明白的是我,可世界上沒有如果,如果當(dāng)初的我再自信點,那么或許會改變什么。(笑) 那時,時有雨天時有晴,但...
    東城吼閱讀 175評論 1 0
  • 本文系原創(chuàng),故事都是真實的。 一 對于還是十三四歲的孩子來說,L同學(xué)長得已經(jīng)算是魁梧了。 但是,他的所有心思幾乎都...
    木凡1980閱讀 405評論 0 0
  • i make people annyoed and tired.but what to do with this,...
    取名字啥的真的好煩閱讀 216評論 0 0

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