Android中的AtomicLong原理、使用与实战指南

 更新时间:2025年03月05日 10:23:01   作者:jiet_h  
本文详细介绍了AtomicLong在Android多线程开发中的应用,包括其核心原理、基本使用、适用场景、生产环境实战案例以及性能优化建议,通过大量Kotlin代码示例,帮助开发者更好地理解和使用AtomicLong,感兴趣的朋友一起看看吧

本文结合生产环境实战案例,带你彻底搞懂AtomicLong在Android多线程开发中的应用。全文包含大量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
        }
    }
}

这个过程就像超市寄存柜——只有当柜子里的物品和预期一致时,才能放入新物品。通过自旋重试机制保证原子性,但要注意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 <= 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 printStats() {
        Log.d("AtomicStats", "CAS失败次数:${casFailureCount.get()}")
    }
}

十、总结

AtomicLong像一把精准的手术刀:

  • 优势:精确控制、API丰富、低延迟
  • 局限:高并发下性能衰减明显(当CAS失败率>30%时需警惕)

到此这篇关于Android中的AtomicLong原理、使用与实战指南的文章就介绍到这了,更多相关Android AtomicLong原理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android计时与倒计时实现限时抢购的5种方法

    Android计时与倒计时实现限时抢购的5种方法

    这篇文章主要为大家详细介绍了Android计时与倒计时实现限时抢购的5种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • android studio集成极光推送的操作步骤

    android studio集成极光推送的操作步骤

    这篇文章主要介绍了android studio集成极光推送的操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03
  • Android实现银行卡号扫描识别功能

    Android实现银行卡号扫描识别功能

    这篇文章主要为大家详细介绍了Android实现银行卡号扫描识别功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-09-09
  • Android 中不用线程如何实现倒计时

    Android 中不用线程如何实现倒计时

    本文给大家分享android中不用线程如何实现倒计时功能,非常不错,具有参考借鉴价值,需要的朋友参考下
    2017-01-01
  • Android实现文字逐字显示出来

    Android实现文字逐字显示出来

    这篇文章主要为大家详细介绍了Android实现文字逐字显示出来效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • android fm单体声和立体声的切换示例代码

    android fm单体声和立体声的切换示例代码

    切换是需要在一定的条件下满足才会进行切换,切换的条件和电台的信号强度RSSI、信号稳定性CQI等等都有关系
    2013-06-06
  • Android三种网络通讯方式及Android的网络通讯机制

    Android三种网络通讯方式及Android的网络通讯机制

    在android平台目前提供了三种网络接口可以使用:分别是java.net.*(标准Java接口)、Org.apache接口和Android.net.*(Android网络接口),本文主要给大家介绍android三种网络通讯方式及android的网络通讯机制,小伙伴们一起学习吧
    2015-11-11
  • Android编程实现切换imageView的方法分析

    Android编程实现切换imageView的方法分析

    这篇文章主要介绍了Android编程实现切换imageView的方法,结合具体实例形式分析了切换imageView的相关设置技巧与注意事项,需要的朋友可以参考下
    2017-09-09
  • Android星级评分条实现评分界面

    Android星级评分条实现评分界面

    这篇文章主要为大家详细介绍了Android星级评分条实现评分界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Android事件分发机制深入刨析原理及源码

    Android事件分发机制深入刨析原理及源码

    Android 的事件分发机制大体可以分为三部分:事件生产、事件分发 、事件消费。事件的生产是由用户点击屏幕产生,我们这次着重分析事件的分发和消费,因为事件分发和处理联系的过于紧密,这篇文章将把事件的分发和消费放在一起分析
    2023-04-04

最新评论