关于java:RxJava和观察者代码的并行执行

RxJava and parallel execution of observer code

我正在使用RxJava Observable api使用以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Observable<Info> observable = fileProcessor.processFileObservable(processedFile.getAbsolutePath());
    observable
      .buffer(10000)
      .observeOn(Schedulers.computation())
      .subscribe(recordInfo -> {
        _logger.info("Running stage2 on thread with id :" + Thread.currentThread().getId());
          for(Info info : recordInfo) {
            // some I/O operation logic
         }
      },
      exception -> {
      },
      () -> {
      });

我的期望是观察代码,即我指定计算调度程序后将并行执行subscribe()方法中的代码。相反,该代码仍在单线程上按顺序执行。如何使代码可以使用RxJava api并行运行。


RxJava在异步/多线程方面经常被误解。多线程操作的编码很简单,但是了解抽象是另一回事。

有关RxJava的一个常见问题是如何实现并行化,或如何从Observable同时发出多个项目。当然,此定义违反了Observable Contract,该协议规定onNext()必须被顺序调用,并且一次不能由多个线程同时调用。

要实现并行性,您需要多个Observable。

这在一个线程中运行:

1
2
3
4
5
6
7
Observable<Integer> vals = Observable.range(1,10);

vals.subscribeOn(Schedulers.computation())
          .map(i -> intenseCalculation(i))
          .subscribe(val -> System.out.println("Subscriber received"
                  + val +" on"
                  + Thread.currentThread().getName()));

这在多个线程中运行:

1
2
3
4
5
6
Observable<Integer> vals = Observable.range(1,10);

vals.flatMap(val -> Observable.just(val)
            .subscribeOn(Schedulers.computation())
            .map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));

代码和文本来自此博客文章。


RxJava 2.0.5引入了并行流和ParallelFlowable,这使得并行执行更简单且更具声明性。

您不再需要在flatMap内创建Observable / Flowable,只需在Flowable上调用parallel()并返回ParallelFlowable

它的功能不如常规的Flowable丰富,因为并发引发了Rx合同的许多问题,但是您拥有基本的map()filter()等,在大多数情况下就足够了。

因此,而不是来自@LordRaydenMK的流程:

1
2
3
4
5
6
Observable<Integer> vals = Observable.range(1,10);

vals.flatMap(val -> Observable.just(val)
        .subscribeOn(Schedulers.computation())
        .map(i -> intenseCalculation(i))
    ).subscribe(val -> System.out.println(val));

现在您可以执行以下操作:

1
2
3
4
5
6
7
Flowable<Integer> vals = Flowable.range(1, 10);

vals.parallel()
        .runOn(Schedulers.computation())
        .map(i -> intenseCalculation(i))
        .sequential()
        .subscribe(val -> System.out.println(val));


为此必须指定subscribeOn(Schedulers.computation())而不是observeOn(Schedulers.computation())
subscribeOn中,声明要在哪个线程中发出值。
observeOn中,声明要在哪个线程中处理并观察它们。


使用flatMap并指定在Schedulers.computation()上进行订阅将实现并发性。

这是一个使用Callable的更实际的示例,从输出中我们可以看到完成所有任务大约需要2000毫秒。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static class MyCallable implements Callable<Integer> {

    private static final Object CALLABLE_COUNT_LOCK = new Object();
    private static int callableCount;

    @Override
    public Integer call() throws Exception {
        Thread.sleep(2000);
        synchronized (CALLABLE_COUNT_LOCK) {
            return callableCount++;
        }
    }

    public static int getCallableCount() {
        synchronized (CALLABLE_COUNT_LOCK) {
            return callableCount;
        }
    }
}

private static void runMyCallableConcurrentlyWithRxJava() {
    long startTimeMillis = System.currentTimeMillis();

    final Semaphore semaphore = new Semaphore(1);
    try {
        semaphore.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Observable.just(new MyCallable(), new MyCallable(), new MyCallable(), new MyCallable())
            .flatMap(new Function<MyCallable, ObservableSource< ? >>() {
                @Override
                public ObservableSource< ? > apply(@NonNull MyCallable myCallable) throws Exception {
                    return Observable.fromCallable(myCallable).subscribeOn(Schedulers.computation());
                }
            })
            .subscribeOn(Schedulers.computation())
            .subscribe(new Observer<Object>() {
                @Override
                public void onSubscribe(@NonNull Disposable d) {

                }

                @Override
                public void onNext(@NonNull Object o) {
                    System.out.println("onNext" + o);
                }

                @Override
                public void onError(@NonNull Throwable e) {

                }

                @Override
                public void onComplete() {
                    if (MyCallable.getCallableCount() >= 4) {
                        semaphore.release();
                    }
                }
            });


    try {
        semaphore.acquire();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        semaphore.release();
    }
    System.out.println("durationMillis" + (System.currentTimeMillis()-startTimeMillis));
}

这仍然是相同的顺序。即使在新线程上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    Observable<Integer> ob3 = Observable.range(1, 5);

    ob3.flatMap(new Func1<Integer, Observable<Integer>>() {

        @Override
        public Observable<Integer> call(Integer pArg0) {

            return Observable.just(pArg0);
        }

    }).subscribeOn(Schedulers.newThread()).map(new Func1<Integer, Integer>() {

        @Override
        public Integer call(Integer pArg0) {

            try {
                Thread.sleep(1000 - (pArg0 * 100));
                System.out.println(pArg0 +"  ccc  " + Thread.currentThread().getName());
            } catch (Exception e) {
                e.printStackTrace();
            }

            return pArg0;
        }

    }).subscribe();

输出

1 ccc RxNewThreadScheduler-1

2 ccc RxNewThreadScheduler-1

3 ccc RxNewThreadScheduler-1

4 ccc RxNewThreadScheduler-1

5 ccc RxNewThreadScheduler-1