如何修复Kotlin中的并发修改异常?附相关代码示例
修复RxJava列表操作中的并发修改异常
首先,咱们先理清楚你代码里可能触发并发修改异常的原因,然后一步步解决问题:
常见触发场景
从你的代码片段来看,大概率是以下两种情况之一:
- 你在遍历非线程安全的输入列表
listOfDocId(比如普通ArrayList)的同时,又对它进行了添加/删除操作; - 多个线程同时操作同一个非线程安全的集合(比如
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
相关产品推荐
相关产品推荐

