阻止RxJava映射内部无法完成

时间:2017-01-19 00:31:24

标签: rx-java deadlock blocking

以下(使用RxJava 1.2.4的错误方法)代码无法解除阻止,永远不会完成。

Scheduler scheduler = Schedulers.computation();
Observable.range(0, 100).map(i -> {
    System.out.println("onNext " + i);
    return Observable.just(i).subscribeOn(scheduler).toBlocking().single();
}).subscribeOn(scheduler).toBlocking().subscribe();
System.out.println("finished");

如果将第一行更改为固定的线程池,则完成。

Scheduler scheduler = Schedulers.from(Executors.newFixedThreadPool(8));

使第一个例子不起作用的计算调度程序有什么特别之处?

1 个答案:

答案 0 :(得分:11)

不要那样做

请注意有关计算调度程序的文档:

  

这可以用于事件循环,处理回调和其他计算工作。 不要在此计划程序上执行IO限制工作

他们想说:不要在此计划程序上执行任何阻止活动

所以你所做的事情是非法的,但它可以作为一个很好的示范。

为什么发生死锁

RxJava 2中发生了同样的死锁(此时为2.0.4)。

由于computation调度程序的实现方式而发生。它创建固定数量的单线程工作者(其数量是CPU核心数;在我的情况下为4)。它为这些工作人员分配任务的方式很简单循环法。现在让我们看看在您的示例中将哪些任务分配给哪些工作人员。

  • worker 1< - subscribe()调用调用循环以在range中生成整数;请注意,此任务未完成,直到所有值都传递到下游
  • worker 2< - just(0)...toBlocking().single()表示第一个生成的整数;由于价值已经可用,因此这一点立即完成而没有实际阻止
  • worker 3< - just(1)...toBlocking().single()表示第二个生成的整数;这一个立即完成
  • worker 4< - just(2)...toBlocking().single()表示第三个生成的整数;这一个立即完成

此时我们 worker 1 仍在忙着range循环, worker 2-4 闲置。下一个任务来自循环,它根据循环分配给 worker1

  • worker 1< - just(3)...toBlocking().single()表示第四个生成的整数;这一个排队,而 worker 1 循环等待其结果。这是死锁。

FixedThreadPool调度程序没有锁定,因为它将任务分配给可用线程,而不是以循环方式。只要确保它有超过1个线程。

阻止是​​邪恶的

通常,您应该避免在Rx管道中阻止操作。 Rx提供了很好的工具来执行异步任务而不会阻塞。您可以使用map代替flatMap,例如:

Scheduler scheduler = Schedulers.computation();
Observable.range(0, 100).flatMap(i -> {
    System.out.println("onNext " + i);
    return Observable.just(i).subscribeOn(scheduler);
}).subscribeOn(scheduler).toBlocking().subscribe();
System.out.println("finished");

即使使用单线程调度程序,这也能正常工作。

您可以调用真正的异步任务,而不是Observable.just(i),而不是Observable.fromFuture(asyncService(i))

如果必要,请使用concatMap代替flatMap来保留商品的订购。