线程安全集合 → 协程安全替代

线程安全集合 → 协程安全替代

老写法(Java)

java 复制代码
// 线程安全的 List
List<String> list = Collections.synchronizedList(new ArrayList<>());
synchronized (list) {
    for (String item : list) {
        // 遍历时仍需要手动同步
    }
}

// 线程安全的 Map
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("key", 1);
Integer value = map.get("key");

// CopyOnWriteArrayList --- 写时复制,读多写少适用
CopyOnWriteArrayList<String> cowList = new CopyOnWriteArrayList<>();
cowList.add("item");

// BlockingQueue --- 生产者-消费者模式
BlockingQueue<String> queue = new LinkedBlockingQueue<>();
queue.put("task");  // 队列满时阻塞
String task = queue.take();  // 队列空时阻塞

问题在哪里

Collections.synchronizedList 只保护单个方法调用,遍历时仍需要手动 synchronized 块,容易漏加导致并发异常。ConcurrentHashMap 粒度细但 API 复杂。BlockingQueue 阻塞的是线程,在协程中直接阻塞线程会浪费线程资源。

新写法(Kotlin + Coroutines)

kotlin 复制代码
// 单线程访问 --- 协程天然单线程处理
// ViewModel 中,所有对同一数据的操作都通过 viewModelScope
private val _items = MutableStateFlow<List<String>>(emptyList())

fun addItem(item: String) {
    _items.update { current ->
        current + item  // 内部线程安全,不需要显式锁
    }
}

// 生产者-消费者 --- Channel 替代 BlockingQueue
private val channel = Channel<String>(capacity = Channel.BUFFERED)

viewModelScope.launch {
    // 生产者
    channel.send("task")  // 挂起,不阻塞线程
}

viewModelScope.launch {
    // 消费者
    for (task in channel) {
        processTask(task)
    }
}

// 并发限制 --- Semaphore
private val semaphore = Semaphore(3)

viewModelScope.launch {
    semaphore.withPermit {
        doLimitedWork()
    }
}

// 读写场景 --- Mutex
private val mutex = Mutex()

suspend fun updateSafely(block: () -> Unit) {
    mutex.withLock {
        block()
    }
}

一句话注意

MutableStateFlow.update {} 内部是原子操作,读-改-写三个步骤不会被其他协程打断,不需要额外加锁。但 StateFlow.value = list + item 这种两步操作就不是原子的了,两个协程同时读再写会丢数据,应始终用 update {}

Channel.send() 是挂起函数,消费者没在监听时生产者会被挂起等待,不消耗线程。这是 Channel 比 BlockingQueue 更适合协程的关键区别------BlockingQueue 会真的阻塞线程,Channel 只是挂起协程。

Mutex.withLock 也是挂起的,不像 Java synchronized 会阻塞线程,所以 Main 线程用 Mutex 也安全。


Java Android 老项目迁移系列,持续更新中。

相关推荐
消失的旧时光-194315 分钟前
Android 系统层扫盲 05:Android 开机后发生了什么?从 Bootloader 到 Launcher
android·zygote·fork·aosp·cow
杨运交20 分钟前
[069][公共模块]Spring Boot 全局异常处理与参数校验实战(下):校验异常精细化处理与 WebFlux 适配
java·spring boot·后端
Raas10040 分钟前
MAI Gateway(魔芋企业级AI网关)对比分析:AI网关和OpenRouter区别?企业级能力差距一览
java·服务器·网络·人工智能·gateway·ai网关·mai gateway
APItesterCris1 小时前
告别人工盯品!借助 Open‑Claw 快速搭建电商商品全自动监控与数据分析系统(完整实操代码)
java·大数据·前端·数据库
青山木1 小时前
Hot 100 --- 跳跃游戏 II
java·数据结构·算法·leetcode·贪心算法
ly76891 小时前
Spring 中的 @Configuration 与 @Component 差异:为何代理时机决定 Bean 生命周期行为
java·后端·spring·注解·代理·bean生命周期
qq_452396231 小时前
第十二篇:《数据采集:Grafana Alloy、Fluent Bit、Vector 的选型与配置》
java·贪心算法·grafana
hunterandroid1 小时前
Android 线上卡顿治理:从布局层级到主线程负载的全链路排查
android
珍珠先生1 小时前
P16 · IDEA 启动报错:找不到或无法加载主类
java
子非鱼a1 小时前
【WEB】[RoarCTF 2019]Easy Java
java·开发语言