如何从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
相关产品推荐
相关产品推荐

