我的jdk源碼(二十四):AtomicInteger類和CAS機制

一、概述

    AtomicInteger類處於java.util.concurrent.atomic包下,與其他原子操作的類一樣,底層都是採用CAS機制,調用了Unsafe類的CAS方法實現的。我們先分析一下AtomicInteger類的源碼,再來分析CAS機制的種種。

二、源碼分析

    1. 類的聲明

public class AtomicInteger extends Number implements java.io.Serializable

    繼承了 Number, 這主要是提供方法將數值轉化爲 byte, double 等方便 Java 開發者使用; 實現了 Serializable, 爲了網絡傳輸等的序列化用, 編碼時最好手動生成序列化 ID, 讓 javac 編譯器生成開銷大, 而且可能造成意想不到的狀況。

    2. 成員變量

    //序列化標識id
    private static final long serialVersionUID = 6214790243416807050L;

    //Unsafe類的一個實例,提供一些不安全的操作的方法用於直接操作內存,一般不會在自己的程序中使用該類
    //在這裏主要用到其中的objectFieldOffset、putOrderedInt、compareAndSwapInt方法
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    //value成員屬性的內存地址相對於對象內存地址的偏移量
    private static final long valueOffset;

    ////初始化valueOffset,通過unsafe.objectFieldOffset方法獲取成員屬性value內存地址相對於對象內存地址的偏移量
    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    //int的值,volatile修飾,保證線程之間的可見性
    private volatile int value;

     Unsafe類是一個JDK內部使用的專屬類,我們自己的應用程序無法直接使用Unsafe類。通過觀察源碼,可以知道獲得Unsafe實例的方法是調動其工廠方法getUnsafe(),源碼如下:

    @CallerSensitive
    public static Unsafe getUnsafe() {
        Class var0 = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
            throw new SecurityException("Unsafe");
        } else {
            return theUnsafe;
        }
    }

    它會檢查調用getUnsafe()函數的類,判斷調用這的類加載器是否是系統類加載器(系統類加載器爲null),如果不是就直接拋出異常,拒絕工作,這也是爲什麼我們寫的程序沒法直接使用這個類的原因。

    3. 構造函數

    //int參數類型的構造函數
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    //無參構造函數,初始值是0
    public AtomicInteger() {
    }

    4. 其他方法

    /**
     * 獲取int值
     */
    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) {
        //樂觀鎖,非阻塞同步方式,循環調用compareAndSet,也就是自旋,直到成功
        for (;;) {
            int current = get();
            //CAS操作,期待值current與內存中的值比較,相等的話設爲newValue值
            //否則下個循環,調用get()獲取current值,繼續執行CAS操作直到成功
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    /**
     * CAS操作,現代CPU已廣泛支持,是一種原子操作;
     * 簡單地說,當期待值expect與valueOffset地址處的值相等時,設置爲update值
     */
    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() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    //調用unsafe.getAndAddInt方法進行數值操作
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    //調用unsafe.getAndAddInt方法進行數值操作
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //調用unsafe.getAndAddInt方法進行數值操作
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    //調用unsafe.getAndAddInt方法進行數值操作
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    //調用unsafe.getAndAddInt方法進行數值操作
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }

    /**
     * 原子操作,自增,返回舊值
     */
    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;
        }
    }

    /**
     * Returns the String representation of the current value.
     * @return the String representation of the current value.
     */
    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();
    }

}

    5. 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;
    }

    第一個參數var1爲給定的對象,var2爲對象內的偏移量(其實就是一個字段到對象頭部的偏移量,通過這個偏移量可以快速定位字段),var4表示期望值,var5表示要設置的值。如果指定的字段的值等於var4,那麼就會把它設置爲var5。

    6. CAS機制原理

    CAS全拼又叫做compareAndSwap,從名字上的意思就知道是比較交換的意思。它包含 3 個參數 CAS(V,E,N),V表示要更新變量的值,E表示預期值,N表示新值。僅當 V值等於E值時,纔會將V的值設爲N,如果V值和E值不同,則說明已經有其他線程做兩個更新,則當前線程則什麼都不做。最後,CAS 返回當前V的真實值。

    7. CAS的優勢

    CAS是一種樂觀鎖,而且是一種非阻塞的輕量級的樂觀鎖。當一個線程想要獲得鎖,對方會給一個迴應表示這個鎖能不能獲得。在資源競爭不激烈的情況下性能高,相比synchronized重量鎖,synchronized會進行比較複雜的加鎖,解鎖和喚醒操作。

    8. CAS存在的問題

    (1) ABA問題

    * 問題描述:CAS需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。

    * 解決辦法:

        a. 用CAS的一個變種DCAS,DCAS是對於每一個V增加一個引用的表示修改次數的標記符。對於每個V,如果引用修改了一次,這個計數器就加1,然後再這個變量需要更新的時候,就同時檢查變量的值和計數器的值。都符合條件纔可以修改V值。

        b. 通過將標記與要進行CAS操作的每個值相關聯,並原子地更新值和標記。也就是一旦V第一次被使用,就不會再重複使用,如有需要則分配新的V。垃圾收集器可以檢查V,保證其不被循環使用,直到當前的訪問操作全部結束。使用 AtomicStampedReference 來解決CAS中的ABA問題,它不再像compareAndSet方法 中只比較內存中的值也當前值是否相等,而且先比較引用是否相等,然後比較值是否相等。

        c. 設置一個版本號,每次修改都會修改版本號,每次對比的時候不僅對比值,還要對比版本號來保證數據沒被修改。

    (2) 自旋時間長導致開銷大

    * 問題描述:自旋 CAS 如果長時間不成功,會給 CPU 帶來非常大的執行開銷。

    * 解決辦法:如果JVM能支持處理器提供的 pause 指令那麼效率會有一定的提升,pause 指令有兩個作用,第一它可以延遲流水線執行指令(de-pipeline),使 CPU 不會消耗過多的執行資源,延遲的時間取決於具體實現的版本,在一些處理器上延遲時間是零。第二它可以避免在退出循環的時候因內存順序衝突(memory order violation:內存順序衝突一般是由僞/假共享引起,假共享是指多個 CPU 同時修改同一個緩存行的不同部分而引起其中一個CPU的操作無效,當出現這個內存順序衝突時,CPU必須清空流水線)而引起 CPU 流水線被清空(CPU pipeline flush),從而提高 CPU 的執行效率。

三、總結

    通過本篇博文,對原子包java.util.concurrent.atomic下的AtomicInteger類進行了分析,其他的類也比較類似,都是底層採用了CAS機制完成各種操作。也學習了CAS的原理和存在的問題以及解決辦法。

    更多精彩內容,敬請掃描下方二維碼,關注我的微信公衆號【Java覺淺】,獲取第一時間更新哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章