我有一个Observable列表,每个Observable都已订阅在一个池线程调度程序上,我将Observable列表合并为一个Observable Observable.merge(observables)。合并结果可观察到的doOnNext方法是否是线程安全的?代码示例如下。
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setDaemon(true)
.setNameFormat("pooled-%s")
.build();
ExecutorService executorService = Executors.newCachedThreadPool(threadFactory);
List<Observable<String>> observableList = Stream.of("1", "2", "3", "4")
.map(o -> {
Observable<String> ob = Observable.create(
emitter -> {
Thread.sleep(new Random().nextInt(50));
System.out.println("emitter:" + o + " Thread:" + Thread.currentThread().getName());
emitter.onNext(o);
emitter.onComplete();
});
return ob.subscribeOn(Schedulers.from(executorService));
}
).collect(Collectors.toList());
List<String> result = new ArrayList<>(); //is need thread safe list here ?
Observable.merge(observableList)
.doOnNext(e -> {
Thread.sleep(new Random().nextInt(50));
System.out.println("doOnNext:" + e + " Thread:" + Thread.currentThread().getName());
result.add(e);
})
.blockingLast();
情况1: 当我运行输出时,似乎是信号线程调用了doOnNext。
emitter:2 Thread:pooled-1
emitter:3 Thread:pooled-2
emitter:4 Thread:pooled-3
emitter:1 Thread:pooled-0
doOnNext:2 Thread:pooled-1
doOnNext:1 Thread:pooled-1
doOnNext:3 Thread:pooled-1
doOnNext:4 Thread:pooled-1
情况2: 当我逐步调试时,输出改变了,似乎由多线程调用了doOnNext。
emitter:1 Thread:pooled-0
doOnNext:1 Thread:pooled-0
emitter:4 Thread:pooled-3
emitter:3 Thread:pooled-2
emitter:2 Thread:pooled-1
doOnNext:4 Thread:pooled-3
doOnNext:2 Thread:pooled-3
doOnNext:3 Thread:pooled-3
我很困惑。
答案 0 :(得分:0)
流是顺序的,并保证在操作符中不重叠,尤其是在组合/协调操作符中。但是,这并不排除线程之间发生doOnNext
“跳跃”。如果要确保doOnNext
始终在同一线程上运行,请在其之前使用observeOn
。
我没有看到任何代码可以在发射器和doOnNext之间切换线程,所以如果我错过了相同的代码,为什么会出现“情况1”。
流可以从任意数量的线程中驱动merge
并推动它。
在这种情况下,doOnNext是否不安全,结果列表应使用线程安全列表。
doOnNext
在输入参数和执行方面是线程安全的。如果您的回调是有状态的或捕获了全局状态,则多个流实现可能会发生冲突。这些必须通过外部方式进行同步。
内部使用ArrayList的Observable.toList方法在这种情况下存在并发问题。
toList
保证不会重叠驱动,并且其内部ArrayList
是针对每个订户的,因此不存在并发问题。