Android中的AtomicLong原理、使用與實(shí)戰(zhàn)指南

本文結(jié)合生產(chǎn)環(huán)境實(shí)戰(zhàn)案例,帶你徹底搞懂AtomicLong在Android多線程開發(fā)中的應(yīng)用。全文包含大量Kotlin代碼示例,建議收藏備用。
一、為什么需要AtomicLong?
在Android開發(fā)中,當(dāng)多個(gè)線程同時(shí)操作同一個(gè)Long型變量時(shí),你可能會(huì)遇到這樣的詭異場(chǎng)景:
var counter = 0L
fun increment() {
// 這個(gè)操作在并發(fā)場(chǎng)景下會(huì)出錯(cuò)!
counter++
}這個(gè)簡(jiǎn)單的自增操作,編譯后會(huì)變成多條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
}
}
}這個(gè)過程就像超市寄存柜——只有當(dāng)柜子里的物品和預(yù)期一致時(shí),才能放入新物品。通過自旋重試機(jī)制保證原子性,但要注意CPU資源消耗。
2.2 內(nèi)存可見性
通過volatile關(guān)鍵字保證修改的可見性:
// JDK源碼片段
private volatile long value;
public final long get() {
return value;
}這個(gè)設(shè)計(jì)讓所有線程都能立即看到最新值。
三、AtomicLong的基本使用
3.1 初始化方式
// 初始值為0 val atomicCounter = AtomicLong() // 帶初始值 val pageViewCounter = AtomicLong(1000)
3.2 常用方法詳解
| 方法名 | 等價(jià)操作 | 說明 |
|---|---|---|
| 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的適用場(chǎng)景
? 推薦使用場(chǎng)景
- 低并發(fā)的精確計(jì)數(shù)器(如頁面訪問量統(tǒng)計(jì))
- 需要保證原子性的狀態(tài)標(biāo)記(如下載進(jìn)度百分比)
- 需要配合其他原子類構(gòu)建復(fù)雜邏輯
不推薦場(chǎng)景
- 超高并發(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)
// 注意:這個(gè)方法要在后臺(tái)線程調(diào)用
fun trackVisit() {
visitCount.incrementAndGet()
if (visitCount.get() % 100 == 0L) {
uploadToServer() // 每100次上報(bào)服務(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()會(huì)導(dǎo)致緩存失效
- 慎用lazySet:只有在明確不需要立即可見時(shí)使用
- 注意自旋消耗:高并發(fā)下考慮退避策略或改用LongAdder
七、與LongAdder的抉擇
當(dāng)遇到類似需求時(shí):
when {
writeQPS < 1000 -> AtomicLong()
writeQPS > 5000 -> LongAdder()
else -> 根據(jù)業(yè)務(wù)精度要求選擇
}八、常見坑點(diǎn)排查
8.1 原子性誤解
錯(cuò)誤用法:
if (atomicValue.get() > 100) {
atomicValue.set(0) // 這兩個(gè)操作不是原子的!
}正確姿勢(shì):
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() // 最后會(huì)變成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)勢(shì):精確控制、API豐富、低延遲
- 局限:高并發(fā)下性能衰減明顯(當(dāng)CAS失敗率>30%時(shí)需警惕)
到此這篇關(guān)于Android中的AtomicLong原理、使用與實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Android AtomicLong原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android計(jì)時(shí)與倒計(jì)時(shí)實(shí)現(xiàn)限時(shí)搶購的5種方法
這篇文章主要為大家詳細(xì)介紹了Android計(jì)時(shí)與倒計(jì)時(shí)實(shí)現(xiàn)限時(shí)搶購的5種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Android實(shí)現(xiàn)銀行卡號(hào)掃描識(shí)別功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)銀行卡號(hào)掃描識(shí)別功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-09-09
Android 中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)
本文給大家分享android中不用線程如何實(shí)現(xiàn)倒計(jì)時(shí)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-01-01
Android三種網(wǎng)絡(luò)通訊方式及Android的網(wǎng)絡(luò)通訊機(jī)制
在android平臺(tái)目前提供了三種網(wǎng)絡(luò)接口可以使用:分別是java.net.*(標(biāo)準(zhǔn)Java接口)、Org.apache接口和Android.net.*(Android網(wǎng)絡(luò)接口),本文主要給大家介紹android三種網(wǎng)絡(luò)通訊方式及android的網(wǎng)絡(luò)通訊機(jī)制,小伙伴們一起學(xué)習(xí)吧2015-11-11
Android編程實(shí)現(xiàn)切換imageView的方法分析
這篇文章主要介紹了Android編程實(shí)現(xiàn)切換imageView的方法,結(jié)合具體實(shí)例形式分析了切換imageView的相關(guān)設(shè)置技巧與注意事項(xiàng),需要的朋友可以參考下2017-09-09
Android星級(jí)評(píng)分條實(shí)現(xiàn)評(píng)分界面
這篇文章主要為大家詳細(xì)介紹了Android星級(jí)評(píng)分條實(shí)現(xiàn)評(píng)分界面,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Android事件分發(fā)機(jī)制深入刨析原理及源碼
Android?的事件分發(fā)機(jī)制大體可以分為三部分:事件生產(chǎn)、事件分發(fā)?、事件消費(fèi)。事件的生產(chǎn)是由用戶點(diǎn)擊屏幕產(chǎn)生,我們這次著重分析事件的分發(fā)和消費(fèi),因?yàn)槭录职l(fā)和處理聯(lián)系的過于緊密,這篇文章將把事件的分發(fā)和消費(fèi)放在一起分析2023-04-04

