Android中的AtomicLong原理、使用與實(shí)戰(zhàn)指南
本文結(jié)合生產(chǎn)環(huán)境實(shí)戰(zhàn)案例,帶你徹底搞懂AtomicLong在Android多線程開發(fā)中的應(yīng)用。全文包含大量Kotlin代碼示例,建議收藏備用。
一、為什么需要AtomicLong?
在Android開發(fā)中,當(dāng)多個線程同時操作同一個Long型變量時,你可能會遇到這樣的詭異場景:
var counter = 0L fun increment() { // 這個操作在并發(fā)場景下會出錯! counter++ }
這個簡單的自增操作,編譯后會變成多條JVM指令(ILOAD, LCONST_1, LADD, LSTORE),根本不是原子操作!普通Long變量在多線程環(huán)境下存在安全隱患。
二、AtomicLong的核心原理
2.1 CAS機(jī)制
AtomicLong底層采用CAS(Compare And Swap)算法:
// 偽代碼實(shí)現(xiàn) fun incrementAndGet(): Long { while(true) { val current = get() val next = current + 1 if (compareAndSet(current, next)) { return next } } }
這個過程就像超市寄存柜——只有當(dāng)柜子里的物品和預(yù)期一致時,才能放入新物品。通過自旋重試機(jī)制保證原子性,但要注意CPU資源消耗。
2.2 內(nèi)存可見性
通過volatile關(guān)鍵字保證修改的可見性:
// JDK源碼片段 private volatile long value; public final long get() { return value; }
這個設(shè)計(jì)讓所有線程都能立即看到最新值。
三、AtomicLong的基本使用
3.1 初始化方式
// 初始值為0 val atomicCounter = AtomicLong() // 帶初始值 val pageViewCounter = AtomicLong(1000)
3.2 常用方法詳解
方法名 | 等價操作 | 說明 |
---|---|---|
get() | val = x | 獲取當(dāng)前值 |
set(newValue) | x = new | 直接賦值(慎用!) |
getAndIncrement() | x++ | 先返回舊值再+1(適合計(jì)數(shù)統(tǒng)計(jì)) |
incrementAndGet() | ++x | 先+1再返回新值 |
compareAndSet(expect, update) | CAS操作 | 核心方法,成功返回true |
四、AtomicLong的適用場景
? 推薦使用場景
- 低并發(fā)的精確計(jì)數(shù)器(如頁面訪問量統(tǒng)計(jì))
- 需要保證原子性的狀態(tài)標(biāo)記(如下載進(jìn)度百分比)
- 需要配合其他原子類構(gòu)建復(fù)雜邏輯
不推薦場景
- 超高并發(fā)計(jì)數(shù)器(考慮LongAdder)
- 需要保證連續(xù)性的操作(如ID生成)
五、生產(chǎn)環(huán)境實(shí)戰(zhàn)案例
5.1 頁面訪問量統(tǒng)計(jì)
class PageVisitTracker { private val visitCount = AtomicLong(0) // 注意:這個方法要在后臺線程調(diào)用 fun trackVisit() { visitCount.incrementAndGet() if (visitCount.get() % 100 == 0L) { uploadToServer() // 每100次上報服務(wù)器 } } fun getVisitCount() = visitCount.get() }
5.2 下載進(jìn)度同步
class DownloadManager { private val progress = AtomicLong(0) fun updateProgress(bytes: Long) { progress.addAndGet(bytes) val current = progress.get() if (current % (1024 * 1024) == 0L) { // 每MB更新UI runOnUiThread { updateProgressBar(current) } } } }
六、性能優(yōu)化建議
- 避免濫用get():頻繁調(diào)用get()會導(dǎo)致緩存失效
- 慎用lazySet:只有在明確不需要立即可見時使用
- 注意自旋消耗:高并發(fā)下考慮退避策略或改用LongAdder
七、與LongAdder的抉擇
當(dāng)遇到類似需求時:
when { writeQPS < 1000 -> AtomicLong() writeQPS > 5000 -> LongAdder() else -> 根據(jù)業(yè)務(wù)精度要求選擇 }
八、常見坑點(diǎn)排查
8.1 原子性誤解
錯誤用法:
if (atomicValue.get() > 100) { atomicValue.set(0) // 這兩個操作不是原子的! }
正確姿勢:
while (true) { val current = atomicValue.get() if (current <= 100) break if (atomicValue.compareAndSet(current, 0)) break }
8.2 數(shù)值溢出問題
val MAX = Long.MAX_VALUE val counter = AtomicLong(MAX - 10) repeat(20) { counter.incrementAndGet() // 最后會變成Long.MIN_VALUE }
九、進(jìn)階技巧
9.1 配合Kotlin擴(kuò)展函數(shù)
fun AtomicLong.update(action: (Long) -> Long) { while (true) { val current = get() val newValue = action(current) if (compareAndSet(current, newValue)) return } } // 使用示例 atomicCounter.update { it * 2 }
9.2 性能監(jiān)控方案
class MonitoredAtomicLong( initialValue: Long ) : AtomicLong(initialValue) { private val casFailureCount = AtomicInteger() override fun compareAndSet(expect: Long, update: Long): Boolean { val success = super.compareAndSet(expect, update) if (!success) casFailureCount.incrementAndGet() return success } fun printStats() { Log.d("AtomicStats", "CAS失敗次數(shù):${casFailureCount.get()}") } }
十、總結(jié)
AtomicLong像一把精準(zhǔn)的手術(shù)刀:
- 優(yōu)勢:精確控制、API豐富、低延遲
- 局限:高并發(fā)下性能衰減明顯(當(dāng)CAS失敗率>30%時需警惕)
到此這篇關(guān)于Android中的AtomicLong原理、使用與實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Android AtomicLong原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實(shí)現(xiàn)橫豎屏切換的實(shí)例代碼
本篇文章主要介紹了Android實(shí)現(xiàn)橫豎屏切換的實(shí)例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-06-06Android自定義相機(jī)界面的實(shí)現(xiàn)代碼
這篇文章主要為大家詳細(xì)介紹了Android自定義相機(jī)界面的實(shí)現(xiàn)代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11解決 INSTALL FAILED CONFLICTING PROVIDER的問題方法
這篇文章主要介紹了解決 INSTALL FAILED CONFLICTING PROVIDER的問題方法的相關(guān)資料,需要的朋友可以參考下2017-02-02Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流效果
這篇文章主要為大家詳細(xì)介紹了Android自定義ViewGroup實(shí)現(xiàn)標(biāo)簽流效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06