为什么在嵌套流中使用calculate()调度程序会导致死锁?

时间:2018-04-22 10:40:44

标签: rx-java rx-java2

最近我写了一些复杂的基于RX的流程,并发现它总是在特定情况下产生死锁。我花了几个小时才发现出了什么问题,似乎可以通过这个简单的例子再现:

public static void main(String[] args) throws InterruptedException {
    Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
        .flatMap(x -> Observable.fromIterable(produceMultiple(x)))
        .subscribeOn(Schedulers.computation())
        .subscribe(System.out::println);

    Thread.sleep(50_000);
}

private static List<Integer> produceMultiple(int x) {
    return Observable.range(1, x)
        .flatMap(y -> Observable.fromCallable(() -> {
                Thread.sleep(1000);
                return 10 * x + y;
            }).subscribeOn(Schedulers.computation())
        ).toList().blockingGet();
}

此程序应打印以下值:11,21,22,31,32,33,...,99。通常,值可表示为XY。每个组X中的值的顺序可以是随机的,但组应按升序排序。如果先前仍在计算中,则不应该发出新组(这是我原来的情况)。

问题是,如果运行此代码,您将只看到前几个元素的输出 - 我相信它与处理器数量相关,因为calculate()调度程序使用固定的线程池大小。

你知道它为什么会这样吗?这似乎很奇怪,因为主链数(1,2,3,...)是在池中的单个线程上处理的,而每次使用blockingGet()完成其工作时,其他线程应该是空闲的。

如果你修改任何提供不同调度程序的subscribeOn()(或者一个是calculate()而第二个是不同的),一切正常。此外,如果我从自定义线程池执行器(绑定到4个线程)创建调度程序,它仍然有效!

1 个答案:

答案 0 :(得分:0)

不要在blockingGet调度程序上使用computation,因为您可能会阻止代码其他部分使用的支持线程。您甚至不必在代码中使用它,只需从Observable返回produceMultiple

static void main(String[] args) throws InterruptedException {
    Observable.just(1, 2, 3, 4, 5, 6, 7, 8, 9)
        .flatMap(x -> produceMultiple(x))
        .subscribeOn(Schedulers.computation())
        .subscribe(System.out::println);

    Thread.sleep(50_000);
}

static Observable<Integer> produceMultiple(int x) {
    return Observable.range(1, x)
        .flatMap(y -> Observable.fromCallable(() -> {
                Thread.sleep(1000);
                return 10 * x + y;
            }).subscribeOn(Schedulers.computation())
        );
}