天天看點

【Java8源碼分析】并發包-AtomicInteger

AtomicInteger類是實作了原子操作的Integer,以往對于保證int、double、float等基礎類型的運算原子性,需要采用加鎖的方式。但是為了一個簡單的運算操作采用鎖,在多線程競争嚴重的情況下,會導緻性能降低,是以在java1.5推出了Atomic包,是采用CAS算法實作原子性運算。

一、CPU鎖

CPU鎖有如下3種類型:

(1)處理器自動保證基本記憶體操作的原子性

首先處理器會自動保證基本的記憶體操作的原子性。 處理器保證從系統記憶體當中讀取或者寫入一個位元組是原子的, 意思是當一個處理器讀取一個位元組時, 其他處理器不能通路這個位元組的記憶體位址。

(2)使用總線鎖保證原子性(開銷大)

所謂總線鎖就是使用處理器提供的一個 LOCK# 信号,當一個處理器在總線上輸出此信号時, 其他處理器的請求将被阻塞住, 那麼該處理器可以獨占使用共享記憶體。

(3)用緩存鎖保證原子性

所謂“緩存鎖定”就是如果緩存在處理器緩存行中記憶體區域在 LOCK 操作期間被鎖定,當它執行鎖操作回寫記憶體時,處理器不在總線上聲言 LOCK# 信号,而是修改内部的記憶體位址,并允許它的緩存一緻性機制來保證操作的原子性,因為緩存一緻性機制會阻止同時修改被兩個以上處理器緩存的記憶體區域資料,當其他處理器回寫已被鎖定的緩存行的資料時會起緩存行無效

二、CAS算法

目前的處理器基本都支援CAS(Compare and swap),隻不過每個廠家所實作的算法并不一樣罷了,每一個CAS操作過程都包含三個運算符:一個記憶體位址V,一個期望的值A和一個新值B,操作的時候如果這個位址上存放的值等于這個期望的值A,則将位址上的值賦為新值B,否則不做任何操作。CAS的基本思路就是,如果這個位址上的值和期望的值相等,則給其賦予新值,否則不做任何事兒,但是要傳回原值是多少。

簡單地說,CAS使得同步并不阻塞在程式設計語言層面上,而是阻塞在硬體層面上。

CAS類似資料庫的樂觀鎖。

三、源碼分析

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = L;

    // Unsafe類隻能在JDK内部使用
    private static final Unsafe unsafe = Unsafe.getUnsafe();

    // AtomicInteger中value的偏移值
    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;
    }

    // set()是直接對value進行操作的,不需要CAS,因為單步操作就是原子操作
    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        // 這是一個有序或者有延遲的方法,并且不保證值的改變被其他線程立即看到
        // 但因為value域設定為volatile,故是有效的
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    // 擷取舊值,設定新值
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    // CAS算法包裝
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    // 根據名字判斷是先擷取,還是先操作
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, );
    }
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -);
    }
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, ) + ;
    }
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -) - ;
    }
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }

    // 以下函數采用自旋的方式,計算新值,然後調用CAS判斷是否可以更新
    // 不能更新重新計算
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }
    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }
    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }
}
           

四、CAS的缺點

(1)ABA問題

因為 CAS 需要在操作值的時候檢查下值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,變成了B,又變成了A,那麼使用CAS進行檢查時會發現它的值沒有發生變化,但是實際上卻變化了。ABA問題的解決思路就是使用版本号。

(2)自旋操作有可能開銷很大

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

(3)隻能保證一個共享變量的原子操作

當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖

五、參考

http://blog.csdn.net/wenniuwuren/article/details/48793855