目录
- 一、为什么需要AtomicLong?
- 二、AtomicLong的核心原理
- 2.1 CAS机制
- 2.2 内存可见性
- 三、AtomicLong的基本使用
- 3.1 初始化方式
- 3.2 常用方法详解
- 四、AtomicLong的适用场景
- ✅ 推荐使用场景
- 不推荐场景
- 五、生产环境实战案例
- 5.1 页面访问量统计
- 5.2 下载进度同步
- 六、性能优化建议
- 七、与LongAdder的抉择
- 八、常见坑点排查
- 8.1 原子性误解
- 8.2 数值溢出问题
- 九、进阶技巧
- 9.1 配合Kotlin扩展函数
- 9.2 性能监控方案
- 十、总结
本文结合生产环境实战案例,带你彻底搞懂AtomicLong在android多线程开发中的应用OJDIY。全文包含大量Kotlin代码示例,建议收藏备用。
一、为什么需要AtomicLong?
在Android开发中,当多个线程同时操作同一个Long型变量时,你可能会遇到这样的诡异场景:
var counter = 0L fun increment() { // 这个操作在并发场景下会出错! counter++ }
这个简单的自增操作,编译后会变成多条JVM指令(ILOAD, LCONST_1, LADD, LSTORE),根本不是原子操作!普通Long变量在多线程环境下存在安全隐患。
二、AtomicLong的核心原理
2.1 CAS机制
AtomicLong底层采用CAS(Compare And Swap)算法:
// 伪代码实现 fun incrementAndGet(): Long { while(true) { val current = get() val next = current + 1 if (compareAndSet(current, next)) { return next } } }
这个过程就像超市寄存柜——只有当柜子里的物品和预期一致时,才能放入新物品。通过自旋重试机制保证原python子性,但要注意CPU资源消耗。
2.2 内存可见性
通过volatile关键字保证修改的可见性:
// JDK源码片段 private volatile long value; public final long get() { return value; }
这个设计让所有线程都能立即看到最新值。
三、AtomicLong的基本使用
3.1 初始化方式
// 初始值为0 val atomicCounter = AtomicLong() // 带初始值 val pageViewCounter = AtomicLong(1000)
3.2 常用方法详解
方法名 | 等价操作 | 说明 |
---|---|---|
get() | val = x | 获取当前值 |
set(newValue) | x = new | 直接赋值(慎用!) |
getAndIncrement() | x++ | 先返回旧值再+1(适合计数统计) |
incrementAndGet() | ++x | 先+1再返回新值 |
compareAndSet(expect, update) | CAS操作 | 核心方法,成功返回true |
四、AtomicLong的适用场景
✅ 推荐使用场景
- 低并发的精确计数器(如页面访问量统计)
- 需要保证原子性的状态标记(如下载进度百分比)
- 需要配合其他原子类构建复杂逻辑
不推荐场景
- 超高并发计数器(考虑LongAdder)
- 需要保证连续性的操作(如ID生成)
五、生产环境实战案例
5.1 页面访问量统计
class PageVisitTracker { private val visitCount = AtomicLong(0) // 注意:这个方法要在后台线程调用 fun trackVisit() { visitCount.incrementAndGet() if (visitCount.get() % 100 == 0L) { uploadToServer() // 每100次上报服务器 } } fun getVisitCount() = visitCount.get() }
5.2 下载进度同步
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) } } } }
六、性能优化建议
- 避免滥用get():频繁调用get()会导致缓存失效
- 慎用lazySet:只有在明确不需要立即可见时使用
- 注意自旋消耗:高并发下考虑退避策略或改用LongAdder
七、与LongAdder的抉择
当遇到类似需求时:
when { writeQPS < 1000 -> AtomicLong() writeQPS > 5000 -> LongAdder() else -> 根据业务精度要求选择 }
八、常见坑点排查
8.1 原子性误解
错误用法:
if (atomicValue.get() > 100) { atomicValue.set(0) // 这两个操作不是原子的! }
正确姿编程客栈势:
while (true) { val current = atomicValue.get() if (current <=php 100) break if (atomicValue.compareAndSet(current, 0)) break }
8.2 数值溢出问题
val MAX = Long.MAX_VALUE val counter = AtomicLong(MAX - 10) repeat(20) { counter.incrementAndGet() // 最后会变成Long.MIN_VALUE }
九、进阶技巧
9.1 配合Kotlin扩展函数
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 性能监控方案
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 pr编程客栈intStats() { Log.d("AtomicStats", "CAS失败次数:${casFailureCount.get()}") } }
十、总结
AtomicLong像一把精准的手术刀:
- 优势:精确控制、API丰富、低延迟
- 局限:高并发下性能衰减明显(当CAS失败率>30%时需警惕)
到此这篇关于Android中的AtomicLong原理、使用与实战指南的文章就介绍到这了,更多相关Android AtomicLong原理内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论