2.4 atomic 包原子類
java.util.concurrent.atomic 包提供了一組用於實現原子操作的類。這些類可以用於在多執行緒環境中執行執行緒安全的、不可中斷的原子操作。原子操作類提供了一種穩定可靠的方式執行原子類操作,而不是使用鎖實現。原子操作類適用於各種併發場景,特別在是高併發場景種,原子操作類效能表現非常突出。
atomic 包下面的功能類,效能測試使用到的只有 4 種:分別是 AtomicBoolean、AtomicInteger、AtomicLong 和 LongAdder。
對於前三種,相信你會有一種眼熟的感覺,都有對應的 Java 基礎資料型別,使用方式也很相似,把 Java 運算子換成了方法實現取值和賦值功能。下面逐個介紹幾位主角。
1. AtomicBoolean
AtomicBoolean 類常用於多執行緒環境種對 Boolean 值的操作。使用者可以直接對 AtomicBoolean 物件的值進行修改,而不需要藉助鎖和 synchronized 關鍵字。下面是 AtomicBoolean 常用的功能。
- public AtomicBoolean(boolean initialValue):構造方法,有無參構造方法,等效於該方法 initialValue = false。
- get():獲取 Boolean 值的方法,返回 boolean 值。
- set(boolean newValue):設定新的 boolean 值。
- getAndSet(boolean newValue):獲取當前值,並設定新的值。
- compareAndSet(boolean expect, boolean update):若當前值等於預期,則更新值,返回 true。否則不更新,返回 false。
AtomicBoolean 在效能測試中,通常用來標記執行緒狀態,控制執行緒啟動和停止;資源初始化、重置的狀態標記,減少執行緒間競爭;還可以用來控制併發,限制同時執行某個操作的執行緒數。
2. AtomicInteger
AtomicInteger 類常用於對整數值的執行原子操作。使用者可以對 AtomicInteger 物件進行執行緒安全的加、減操作,同樣的不需要鎖和 synchronized。下面是 AtomicInteger 常用功能。
- public AtomicInteger(int initialValue):構造方法,有無參構造方法,等效於該方法 initialValue = 0。
- get():獲取整數值的方法,返回整數值。
- set(int newValue):設定整數值。
- getAndSet(int newValue):獲取當前值,並重新新的值。
- getAndIncrement() 和 getAndDecrement():獲取當前值,並且對當前值進行加一和減一操作。同胞兄弟 incrementAndGet() 和 decrementAndGet(),區別在於這兩個方法先進行賦值操作後獲取值,類似與 i++ 和 ++i 的區別。
- addAndGet(int delta):在當前值基礎上增加指定量(可以是負值),並獲取新值。
- compareAndSet(int expect, int update):若當前值等於預期值,則重新賦值,返回 true,否則不更新值,返回 false。
- AtomicInteger 主要應用在多執行緒場景中,計數和狀態控制。在效能測試中,通常用來進行資料的計算,例如統計執行任務的數量、使用者對某商品的消費總額等。
3. AtomicLong
AtomicLong 功能跟 AtomicInteger 一樣,區別在於 AtomicLong 可以表達的整數型別範圍更廣。兩者所能處理的整數範圍預期對應基礎資料型別所能表示整數型別範圍一樣。
4. LongAdder
LongAdder 功能與 AtomicLong、AtomicInteger 一模一樣,能處理整數值範圍與 AtomicLong 一致。區別在於 LongAdder 在高併發計數場景中,效能表現更好。原因在於 LongAdder 使用了分段鎖來降低執行緒競爭,這個設計思路跟 ConcurrentHashMap 一致。如果你將來遇到類似場景,可以閱讀這兩個類的原始碼尋找設計靈感。下面介紹一下 LongAdder 的常用功能。
- LongAdder():構造方法,只此一家,預設值為 0。
- add(long x):將當前值增加固定量(可以為負)。
- increment() 和 decrement():將當前值自增一和自減一,等效於 add(1L) 和 add(-1L)。
- sum():獲取當前累計值,由於採取了分段設計,所以方法名是求和(sum)。
- reset():重置當前物件,回覆預設值 0。
- sumThenReset():獲取當前值,並重置。
LongAdder 使用場景跟前兩者基本重合,如果你需要進行高併發計數、大規模資料聚合操作,優先考慮 LongAdder 實現該功能。
對於高併發並沒有一個嚴格的規範,筆者提供一個資料僅供參考:在 500 執行緒併發場景下:LongAdder 優勢比較明顯;在 200 ~ 300 併發場景中,LongAdder 略有優勢;在更低的併發中,兩者效能無明顯差異。
如果在工作中,你仍然無法決斷,後面會講到 Java 微基準測試工具 JMH,可以幫助你在實際場景中做出明智的選擇。
書的名字:從 Java 開始做效能測試 。
如果本書內容對你有所幫助,希望各位不吝讚賞,讓我可以貼補家用。讚賞兩位數可以提前閱讀未公開章節。我也會嘗試製作本書的影片教程,包括必要的答疑。
FunTester 原創精華
【連載】從 Java 開始效能測試
- 混沌工程、故障測試、Web 前端
- 服務端功能測試
- 效能測試專題
- Java、Groovy、Go
- 白盒、工具、爬蟲、UI 自動化
- 理論、感悟、影片