首頁  >  文章  >  Java  >  AtomicInteger原子類別的作用介紹(程式碼範例)

AtomicInteger原子類別的作用介紹(程式碼範例)

不言
不言轉載
2019-03-02 14:49:002851瀏覽

這篇文章帶給大家的內容是關於AtomicInteger原子類別的作用介紹(程式碼範例),有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。

AtomicInteger 原子類別的作用

多執行緒操作,Synchronized 效能開銷太大count 並不是原子操作。因為count 需要經過讀取-修改-寫入三個步驟。

count 並不是原子操作。因為count 需要經過讀取-修改-寫入三個步驟。

可以這樣做:

public synchronized void increase() {
    count++;
}

Synchronized鎖定是獨佔的,意味著如果有別的執行緒在執行,當前執行緒只能是等待!

用CAS運算

CAS有3個運算元:

記憶體值V舊的預期值A要修改的新值B當多個執行緒嘗試使用CAS同時更新同一個變數時,只有其中一個執行緒能更新變數的值(A和記憶體值V相同時,將記憶體值V修改為B),而其它執行緒都失敗,失敗的執行緒並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試(或什麼都不做)。

我們可以發現CAS有兩種情況:

如果記憶體值V和我們的預期值A相等,則將記憶體值修改為B,操作成功!

如果記憶體值V和我們的預期值A不相等,一般也有兩種情況:

重試(自旋)什麼都不做

理解CAS的核心是:

CAS是原子性的,雖然你可能看到比較後再修改(compare and swap)覺得會有兩個操作,但終究是原子性的!

原子變數類別在java.util.concurrent.atomic套件下,整體來看有這麼多

基本型別:

AtomicBoolean:布林型AtomicInteger:整數AtomicLong:長整型

陣列:

AtomicIntegerArray:陣列裡的整數AtomicLongArray:陣列裡的長整數AtomicReferenceArray:陣列裡的參考類型

#參考類型:

AtomicReference:引用類型AtomicStampedReference:帶有版本號的引用類型AtomicMarkableReference:帶有標記位的引用類型

物件的屬性

AtomicIntegerFieldUpdater:物件的屬性是整型AtomicLongFieldUpdater:物件的屬性是長整型AtomicReferenceFieldUpdater:物件的屬性是參考類型

JDK8新增DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder

是對AtomicLong等類別的改進。例如LongAccumulator與LongAdder在高並發環境下比AtomicLong更有效率。 Atomic套件裡的類別基本上都是使用Unsafe實現的包裝類別

Unsafe裡邊有幾個我們喜歡的方法(CAS):

// 第一和第二个参数代表对象的实例以及地址,第三个参数代表期望值,第四个参数代表更新值
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

原子變數類別使用

class Count{
    // 共享变量(使用AtomicInteger来替代Synchronized锁)
    private AtomicInteger count = new AtomicInteger(0);
    public Integer getCount() {
        return count.get();
    }
    public void increase() {
        count.incrementAndGet();
    }
}

原子類的ABA問題

下面的操作都可以正常執行完的,這樣會發生什麼問題呢? ?線程C無法得知線程A和線程B修改過的count值,這樣是有風險的。

現在我有一個變數count=10,現在有三個線程,分別是A、B、C線程A和線程C同時讀到count變量,所以線程A和線程C的記憶體值和預期值都為10此時線程A使用CAS將count值修改成100修改完後,就在這時,線程B進來了,讀取得到count的值為100(內存值和預期值都是100),將count值修改成10線程C拿到執行權,發現記憶體值是10,預期值也是10,將count值修改成11

解決ABA問題

要解決ABA的問題,我們可以使用JDK給我們的AtomicStampedReference和AtomicMarkableReference類別。

簡單來說就是在提供了一個物件給這個物件,而這個版本如果被修改了,是自動更新的。

原理大概就是:維護了一個Pair對象,Pair物件儲存我們的物件參考和一個stamp值。每次CAS比較的是兩個Pair物件

LongAdder 效能比AtomicLong 要好

使用AtomicLong時,在高並發下大量執行緒會同時去競爭更新同一個原子變量,但是由於同時只有一個執行緒的CAS會成功,所以其他執行緒會不斷嘗試自旋嘗試CAS操作,這會浪費不少的CPU資源。

而LongAdder可以概括成這樣:內部核心資料value分離成一個數組(Cell),每個執行緒存取時,透過哈希等演算法映射到其中一個數字進行計數,而最終的計數結果,則為這個數組的求和累加。

簡單來說就是將一個值分散成多個值,在並發的時候就可以分散壓力,效能有所提升。


以上是AtomicInteger原子類別的作用介紹(程式碼範例)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除