当前位置: 首页 > article >正文

Android中的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) }
        }
    }
}

六、性能优化建议

  1. 避免滥用get():频繁调用get()会导致缓存失效
  2. 慎用lazySet:只有在明确不需要立即可见时使用
  3. 注意自旋消耗:高并发下考虑退避策略或改用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%时需警惕)

http://www.kler.cn/a/570789.html

相关文章:

  • Android 常见View的防抖
  • Python常见面试题的详解10
  • 深入理解 Promise 和 Async/Await,并结合 Axios 实践
  • PPT 小黑第38套
  • MongoDB分片集群
  • 【SRC实战】小游戏漏洞强制挑战
  • K8S学习之基础七:k8s中node污点和pod容忍度
  • safetensors PyTorchModelHubMixin 加载模型
  • 计算机视觉|从0到1揭秘Diffusion:图像生成领域的新革命
  • 【深入剖析:机器学习、深度学习与人工智能的关系】
  • 【C++】使用 CMake 在 Windows 上自动化发布 C++/Qt 应用程序
  • VSCode 移除EmmyLua插件的红色波浪线提示
  • module_init 详解
  • 深刻理解redis高性能之IO多路复用
  • Golang学习笔记_40——模版方法模式
  • Tauri+React跨平台开发环境搭建全指南
  • IDEA 接入 Deepseek
  • 如何使用Docker一键本地化部署LibrePhotos搭建私有云相册
  • Android开发,多宫格列表实现
  • Java——通配符以及上下限