You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中多Flow并发访问同一变量的线程安全性问题

代码线程安全性分析与解决方案

结论

这段代码不具备线程安全性,存在竞态条件问题,必须添加同步机制或改用更安全的Flow组合方式。

问题原因

  1. 共享变量并发读写:listAll、list1、list2是跨协程共享的可变变量,两个子协程会同时对它们进行读写操作。即使在主线程执行,协程调度切换也会破坏操作原子性,导致listAll内容出现错误组合,或是被协程互相覆盖结果。
  2. 非原子性操作:每个collect块内的赋值、列表构建操作不是原子的。比如协程A刚更新完list1,还没完成listAll构建,协程B就更新了list2,此时协程A构建的listAll会错误地使用新list1与旧list2的组合。

解决方案

方案一:手动添加同步锁

用synchronized块包裹所有对共享变量的读写逻辑,确保同一时间只有一个协程能执行这些操作:

viewModelScope.launch {
    val lock = Any() // 专用锁对象
    var listAll = mutableListOf<String>()
    var list1 = mutableListOf<String>()
    var list2 = mutableListOf<String>()

    launch {
        source1.getNames().collect { list ->
            synchronized(lock) {
                list1 = list
                listAll = mutableListOf<String>().apply {
                    addAll(list1)
                    addAll(list2)
                }
                // 后续消费listAll的逻辑(比如emit到StateFlow)
            }
        }
    }

    launch {
        source2.getNames().collect { list ->
            synchronized(lock) {
                list2 = list
                listAll = mutableListOf<String>().apply {
                    addAll(list2)
                    addAll(list1)
                }
                // 后续消费listAll的逻辑(比如emit到StateFlow)
            }
        }
    }
}

方案二:使用Flow的combine操作符(推荐)

Kotlin Flow内置的combine操作符会自动处理两个Flow的同步组合,每次任意一个Flow发射新值时,都会用最新的两个值生成组合结果,无需手动管理锁:

// 先组合两个Flow
val combinedNamesFlow = combine(source1.getNames(), source2.getNames()) { list1, list2 ->
    mutableListOf<String>().apply {
        addAll(list1)
        addAll(list2)
    }
}

// 收集组合后的结果
viewModelScope.launch {
    combinedNamesFlow.collect { listAll ->
        // 消费listAll的逻辑(比如emit到StateFlow)
    }
}

这种方式更符合Kotlin协程与Flow的设计理念,代码更简洁且不易出错。

内容的提问来源于stack exchange,提问作者Ригхард Чан

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:35:33