RxJava中的并行性 - 过滤器

时间:2016-08-10 10:41:34

标签: java multithreading rx-java

我有一些非常简单的代码,读了一堆Strings&应用过滤器。我希望过滤器能在多个线程上运行。

    Iterable<String> outputs = Observable
            .from(Files.readLines(new File("E:\\SAMA\\Test\\ImageNetBullets.txt"), Charset.forName("utf-8")))
            .take(20).subscribeOn(Schedulers.from(threadPoolExecutor)).filter(str -> isURLOK(str))
            .toBlocking().toIterable();

从日志中可以看出,Filter方法只在一个线程上运行:

In Thread pool-1-thread-1
In Thread pool-1-thread-1
http://farm2.static.flickr.com/1258/1479683334_3ff920d217.jpg
In Thread pool-1-thread-1
In Thread pool-1-thread-1

如何加快速度?

2 个答案:

答案 0 :(得分:5)

RxJava本质上是顺序的。例如,使用map(Func1)Func1本身将与通过父序列的值非并发执行:

Observable.range(1, 10).map(v -> v * v).subscribe(System.out::println);

这里,lambda v - &gt; v * v将以顺序方式调用值1到10。

RxJava可以以管道中的阶段(范围 - > map-&gt; subscribe)相对于彼此同时/并行发生的方式异步。例如:

Observable.range(1, 10)
.subscribeOn(Schedulers.computation())
.map(v -> v * v)                       // (1)
.observeOn(Schedulers.io())
.map(v -> -v)                          // (2)
.toBlocking()
.subscribe(System.out::println);       // (3)

这里,(1)可以与(2)和(3)并行运行,即,(2)计算av = 3 * 3,(1)可能已经计算v = 5并且(3)打印出来-1在同一时间。

如果您想同时处理序列的元素,您必须将序列“分叉”到子Observable s,然后将结果与flatMap联接起来:

Observable.range(1, 10)
.flatMap(v -> 
    Observable.just(v)
    .subscribeOn(Schedulers.computation())
    .map(v -> v * v)
)
.toBlocking()
.subscribe(System.out::println);

这里,每个值v将启动一个在后台线程上运行的新Observable,并通过map()进行计算。 v = 1可以在线程1上运行,v = 2可以在线程2上运行,v = 3可以在线程1上运行,但严格地在计算v = 1之后运行。

答案 1 :(得分:2)

.subscribeOn的调用只是确定将启动Scheduler观察点(并且对于您的示例,所有排放将在调度程序提供的一个线程上传播)。

如果您没有太多工作要处理流中的每个项目,那么处理可能由IO主导,因此并行处理可能无济于事。

一般来说,虽然一种方法是将流缓冲到块中并处理flatMap上订阅的Schedulers.computation()内的每个块:

 Observable<String> outputs = 
   lines
    .buffer(1000)
    .flatMap(list -> 
        Observable
          .from(list)
          //do something computationally expensive
          .filter(line -> intensive(line))
          .subscribeOn(Schedulers.computation()));

使用buffer是因为调度大量工作所需的开销比安排大量微小任务要少。