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

如何修复Kotlin中的并发修改异常?附相关代码示例

修复RxJava列表操作中的并发修改异常

首先,咱们先理清楚你代码里可能触发并发修改异常的原因,然后一步步解决问题:

常见触发场景

从你的代码片段来看,大概率是以下两种情况之一:

  1. 你在遍历非线程安全的输入列表listOfDocId(比如普通ArrayList)的同时,又对它进行了添加/删除操作;
  2. 多个线程同时操作同一个非线程安全的集合(比如listOfDocId被外部线程修改,或者filteredList在多线程下被写入)。

具体修复方案

1. 处理输入列表listOfDocId的遍历安全

如果你的逻辑需要遍历listOfDocId的同时修改它(比如删除元素),最稳妥的方式是先复制一份列表副本,遍历副本而非原列表:

// 复制原列表,遍历副本时修改原列表不会触发并发异常
val copiedDocIds = ArrayList(listOfDocId)
for (docId in copiedDocIds) {
    // 这里可以安全地修改原listOfDocId,比如删除符合条件的元素
    if (someCondition) {
        listOfDocId.remove(docId)
    }
}

如果listOfDocId本身会被多个线程共享修改,那直接把它换成线程安全的CopyOnWriteArrayList<String>,从根源避免并发问题。

2. 优化filteredList的使用

你现在用了CopyOnWriteArrayList作为结果列表,但要注意:

  • 如果filteredList只在Observable.create的代码块里单线程操作(比如没有多线程写入),完全没必要用CopyOnWriteArrayList——它的写操作性能很低(每次写都会复制整个数组),换成普通的mutableListOf<SearchResultModel>就足够安全;
  • 只有当你需要在多个线程同时写入filteredList时,才需要保留CopyOnWriteArrayList。

3. 规范RxJava的线程调度

RxJava的线程调度混乱也可能导致并发问题,建议明确指定执行线程和订阅线程:

return Observable.create<MutableList<SearchResultModel>> { emitter ->
    // 这里的代码运行在subscribeOn指定的后台线程
    val filteredList = mutableListOf<SearchResultModel>()
    // 你的处理逻辑...
    emitter.onNext(filteredList)
    emitter.onComplete()
}.subscribeOn(Schedulers.io()) // 指定后台线程执行数据处理
.observeOn(AndroidSchedulers.mainThread()) // 指定主线程接收结果

4. 完整修复后的示例代码

结合上面的方案,修复后的代码大概是这样:

fun bindProcess(listOfDocId: MutableList<String>, isActvityLogEnabled: Boolean): Observable<MutableList<SearchResultModel>> {
    return Observable.create<MutableList<SearchResultModel>> { emitter ->
        // 复制输入列表,避免遍历原列表时修改触发异常
        val copiedDocIds = ArrayList(listOfDocId)
        val filteredList = mutableListOf<SearchResultModel>()

        // 添加头部项
        val headerItem = SearchResultModel().apply {
            type = 0
            title = context.getString(R.string.your_header_content)
        }
        filteredList.add(headerItem)

        // 遍历副本处理每个文档ID
        for (docId in copiedDocIds) {
            val resultItem = SearchResultModel().apply {
                type = 1
                // 填充其他属性,比如docId、内容等
                this.docId = docId
            }
            filteredList.add(resultItem)

            // 如果需要修改原列表,现在操作不会影响遍历
            if (isActvityLogEnabled) {
                listOfDocId.remove(docId)
            }
        }

        emitter.onNext(filteredList)
        emitter.onComplete()
    }.subscribeOn(Schedulers.io())
     .observeOn(AndroidSchedulers.mainThread())
}

额外提醒

  • 尽量避免在RxJava的create块中长时间持有外部集合的引用,处理完就发射结果,减少并发冲突的可能;
  • 如果你的项目用了RxJava 2+,可以考虑用Observable.fromIterable来遍历集合,它内部会处理部分线程安全问题。

内容的提问来源于stack exchange,提问作者MARSH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:59:34