Java LongAdder使用與應(yīng)用實戰(zhàn)
LongAdder是 Java 并發(fā)編程中為高并發(fā)計數(shù)而生的利器。下面這張表格能幫助你快速把握其全貌,之后我們再深入細節(jié)和實戰(zhàn)。
| 特性維度 | 說明 |
|---|---|
| ?設(shè)計目標(biāo)? | 高并發(fā)場景下的高性能計數(shù)/累加操作,優(yōu)化多線程寫競爭 。 |
| ?核心思想? | ?分而治之,分散熱點?:將單一變量的競爭壓力分散到多個單元(base+ Cell[]),以空間換取時間 。 |
| ?關(guān)鍵數(shù)據(jù)結(jié)構(gòu)? | base(基礎(chǔ)值) 和 Cell[](單元數(shù)組) 。 |
| ?寫入流程? | 1. 無競爭或低競爭時,CAS 操作直接更新 base。 2. 競爭激烈時,線程通過哈希映射到 Cell[]中的某個單元進行更新,大幅減少沖突 。 |
?讀取流程 (sum())?? | 返回 base與所有 Cell單元值的累加和。此操作不保證強一致性,是最終一致性的,因為在求和過程中可能有其他線程正在更新 。 |
| ?主要優(yōu)點? | 高并發(fā)寫入性能遠超 AtomicLong,有效減少 CAS 空自旋,避免高競爭下的性能驟降 。 |
| ?主要缺點? | 更高的內(nèi)存消耗;讀取操作 (sum()) 非原子快照,是最終一致性的;不支持 compareAndSet等原子條件更新操作 。 |
| ?典型應(yīng)用場景? | 高頻統(tǒng)計計數(shù)器(如 API 調(diào)用次數(shù)、點擊量)、監(jiān)控指標(biāo)收集、頻率統(tǒng)計等“寫多讀少”且對讀的實時精確性要求不高的場景 。 |
?? 深入核心原理
要理解 LongAdder的高性能,需要深入其內(nèi)部機制。
?分散熱點與動態(tài)擴容?
初始時,所有線程都嘗試通過 CAS 操作更新
base變量。當(dāng)并發(fā)加劇,某個線程 CAS 更新base失敗時,系統(tǒng)會初始化一個Cell數(shù)組(默認大小為 2)。每個線程會根據(jù)其唯一的哈希值(探針,Probe)被映射到數(shù)組的某個槽位(Cell),然后對該槽位內(nèi)的值進行更新 。隨著競爭持續(xù),如果線程在指定的Cell上更新仍然失敗,Cell數(shù)組會進行擴容(通常翻倍),直到達到與 CPU 核數(shù)相當(dāng)?shù)乃剑赃M一步分散競爭 。這種設(shè)計將針對單一內(nèi)存地址的激烈競爭,轉(zhuǎn)化為對多個內(nèi)存地址的相對平和的訪問。?解決偽共享?
Cell類使用@sun.misc.Contended注解進行填充,以避免偽共享?(False Sharing)。偽共享是指多個看似不相關(guān)的變量因位于同一個 CPU 緩存行中,當(dāng)一個處理器更新其中一個變量時,會導(dǎo)致整個緩存行失效,其他處理器即使使用該行內(nèi)的其他變量,也需要重新從內(nèi)存加載,造成性能損失。@Contended注解確保每個Cell對象獨立占據(jù)一個緩存行,從而提升緩存效率 。?核心方法流程?
add(long x)方法是其核心 :- 首先檢查
cells數(shù)組是否已初始化。若未初始化,則嘗試直接 CAS 更新base字段。 - 若 CAS 更新
base失敗(表明出現(xiàn)競爭),則進入沖突處理邏輯。 - 檢查
cells數(shù)組是否已初始化、當(dāng)前線程映射的Cell槽位是否存在、以及嘗試 CAS 更新該Cell的值。 - 如果以上任意一步失敗,則進入更復(fù)雜的
longAccumulate方法。該方法會處理cells數(shù)組的初始化、擴容,以及為線程重新計算哈希值以尋找新的空閑槽位,確保更新最終能夠完成 。
sum()方法遍歷cells數(shù)組(如果已初始化),將所有非空Cell的值與base相加返回。由于此操作沒有加鎖,在并發(fā)更新時返回的是某個時刻的近似總值,具備最終一致性而非強一致性 。- 首先檢查
??? 實戰(zhàn)應(yīng)用示例
LongAdder非常適用于以下場景:
?API 請求統(tǒng)計與監(jiān)控?
可以輕松統(tǒng)計服務(wù)的請求量、成功/失敗次數(shù)、總耗時等指標(biāo) 。
public class ApiRequestMonitor { private final LongAdder requestCount = new LongAdder(); private final LongAdder totalLatency = new LongAdder(); public void recordRequest(long latency) { requestCount.increment(); totalLatency.add(latency); } public MonitoringSnapshot getSnapshot() { // 注意:sum() 獲取的是瞬時近似值 return new MonitoringSnapshot(requestCount.sum(), totalLatency.sum()); } }?結(jié)合 ConcurrentHashMap 進行頻率統(tǒng)計?
這是一種常見且高效的模式,用于統(tǒng)計元素出現(xiàn)次數(shù) 。
ConcurrentMap<String, LongAdder> freqMap = new ConcurrentHashMap<>(); public void count(String word) { // 如果鍵不存在,則原子性地放入一個新的 LongAdder freqMap.computeIfAbsent(word, k -> new LongAdder()) .increment(); // 然后遞增 } // 獲取某個詞的頻率 long frequency = freqMap.getOrDefault(word, new LongAdder()).sum();
?? 使用注意事項與最佳實踐
- ?理解一致性語義?:
LongAdder的sum()方法是最終一致性的。如果你的業(yè)務(wù)場景要求在任何時刻讀取都必須是完全精確的值(例如金融賬戶余額),那么AtomicLong或鎖機制更為合適 。 - ?關(guān)注內(nèi)存占用?:
Cell數(shù)組和避免偽共享的填充會帶來比AtomicLong更高的內(nèi)存開銷。在內(nèi)存受限或并發(fā)度不高的環(huán)境中,需要權(quán)衡利弊 。 - ?避免頻繁調(diào)用
sum()?:sum()方法需要遍歷Cell數(shù)組,在數(shù)組較大時有一定開銷。應(yīng)避免在性能關(guān)鍵路徑中頻繁調(diào)用 。 - ?重置操作?:
reset()方法將base和所有Cell置零,但此操作非原子性。通常僅在確定沒有并發(fā)更新時(如一個統(tǒng)計周期結(jié)束清零時)使用 。
?? 選型指南:LongAdder vs. AtomicLong
| 場景 | 推薦選擇 | 理由 |
|---|---|---|
| ?極高并發(fā)寫入,對讀的實時精確性要求不高?(如統(tǒng)計、監(jiān)控) | ?LongAdder? | 寫吞吐量極高,通過分散競爭避免性能瓶頸 。 |
| ?低并發(fā)環(huán)境,或需要頻繁讀取精確瞬時值?(如序列號生成、狀態(tài)標(biāo)志) | ?AtomicLong? | 讀取 (get()) 是強一致性的單次 volatile 讀,性能極高;接口豐富,支持 compareAndSet等復(fù)雜原子操作 。 |
| ?需要復(fù)雜的累加操作?(如求最大值、最小值) | ?LongAccumulator? | LongAdder是 LongAccumulator的一個特例(專用于加法)。LongAccumulator允許傳入自定義二元運算符,功能更靈活 。 |
?? 總結(jié)
LongAdder是 Java 并發(fā)工具包中“分而治之”思想的杰出代表。它通過空間換時間,巧妙地化解了高并發(fā)下的寫入競爭,在統(tǒng)計、監(jiān)控等“寫多讀少”的場景下表現(xiàn)卓越。
選擇 LongAdder的關(guān)鍵在于明確:?你是否愿意用讀取操作的強一致性和更高的內(nèi)存開銷,來換取極高的并發(fā)寫入性能。
到此這篇關(guān)于Java LongAdder使用與應(yīng)用實戰(zhàn)的文章就介紹到這了,更多相關(guān)Java LongAdder使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解使用MyBatis Generator自動創(chuàng)建代碼
這篇文章主要介紹了使用MyBatis Generator自動創(chuàng)建代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
MybatisPlusException:Failed?to?process,Error?SQL異常報錯的解決辦法
這篇文章主要給大家介紹了關(guān)于MybatisPlusException:Failed?to?process,Error?SQL異常報錯的解決辦法,文中通過實例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2023-03-03

