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

如何从void方法返回Completable?RxJava线程与集合并发问题咨询

问题分析与解答

嗨,我来帮你拆解这个问题,结合你的代码和疑问逐一说明:

1. RxJava默认的线程模型

默认情况下,RxJava 不会自动切换线程——所有操作符(包括你用到的flatMapCompletable)都会在触发订阅的线程上执行,除非你显式用subscribeOn()(指定Observable发射事件的线程)或observeOn()(指定下游操作执行的线程)来调度线程。

简单总结:如果你的上游Observable是在单线程同步发射player事件,那整个流程默认就是单线程同步的;但如果上游是多线程异步发射(比如从线程池、IO线程发射),那flatMapCompletable里的代码就可能在多个线程上并发执行。

2. player代码块的执行方式

你代码里处理player、调用writerObj.write()的flatMapCompletable lambda,是否同步执行完全取决于上游Observable的发射线程和调度器:

  • 如果上游是单线程同步发射(比如Observable.just(...)或者未指定subscribeOn的同步数据源),那么每个player对应的write()调用会依次串行执行,不会有并发冲突。
  • 如果上游是多线程异步发射(比如用了subscribeOn(Schedulers.io())、或者从多个线程发射事件的Observable),那么flatMapCompletable默认会为每个player创建的Completable安排在上游发射该事件的线程执行,这时候多个write()调用就会并发执行。

为什么CopyOnWriteArrayList正常,ArrayList崩溃?

核心原因是线程安全性差异:

  • ArrayList是非线程安全集合,当多个线程同时调用add()时,会破坏内部数据结构(比如数组扩容时的并发修改、索引越界等),进而抛出ConcurrentModificationException或其他运行时异常。
  • CopyOnWriteArrayList是线程安全的并发集合,它的修改操作会复制一份新数组来存储数据,从根源避免了并发修改的冲突,所以即使多线程调用add()也不会崩溃。

改用ArrayList的解决建议

如果你想继续使用ArrayList,可以通过两种方式避免并发问题:

  • 强制write()调用串行执行:在flatMapCompletable里指定单线程调度器,比如:
    .flatMapCompletable(player -> 
        Completable.fromRunnable(() -> writerObj.write(player))
            .subscribeOn(Schedulers.single()) // 用单线程调度器保证串行执行
    )
    
  • 在write()方法内部加同步锁:
    public void write(Player player) {
        synchronized(this) {
            arrayList.add(player);
        }
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:29:44