subscriptionOn(Schedulers.parallel())无法正常工作

时间:2019-01-16 08:12:05

标签: reactive-programming spring-webflux reactive-streams

我正在学习反应堆核心,并遵循此https://www.baeldung.com/reactor-core

ArrayList<Integer> arrList = new ArrayList<Integer>();
System.out.println("Before: " + arrList);
Flux.just(1, 2, 3, 4)
  .log()
  .map(i -> i * 2)
  .subscribeOn(Schedulers.parallel())
  .subscribe(arrList::add);

System.out.println("After: " + arrList);

当我执行上面的代码行时,给出提示。

 Before: []
 [DEBUG] (main) Using Console logging
 After: []

以上代码行应在另一个线程中开始执行,但根本无法正常工作。 有人可以帮我吗?

2 个答案:

答案 0 :(得分:2)

我认为有些混乱。当您致电Foo时。您指定要在其他线程上接收项目。另外,您还必须减慢代码速度,以便实际启动订阅cen(这就是为什么我添加了Bar)。如果您运行我已通过的代码,它将起作用。您会看到反应堆中没有魔术同步机制。

subscribeOn(Schedulers.parallel())

如果要在并行反应器中将项目添加到列表中,不是一个好的选择。最好在Java 8中使用并行流。

Thread.sleep(100)

关于并发部分,您发布的教程并不十分精确。值得称赞的是,他/她说还将发表更多文章。但是我不认为应该发布该特定示例,因为它会造成混乱。我建议不要太信任互联网上的资源:)

答案 1 :(得分:0)

如Reactor文档中针对各种subscribe方法所述:

  

请记住,由于序列可以是异步的,因此这将   立即将控制权返回给调用线程。这可以给   印象在主线程中执行时未调用使用者   或例如单元测试。

这意味着到达main方法的末尾,因此主线程在任何线程能够订阅Reactive链之前退出,如Piotr所述。

您要做的是等到整个链完成后再打印阵列的内容。

天真的做法是:

    ArrayList<Integer> arrList = new ArrayList<>();
    System.out.println("Before: " + arrList);
    Flux.just(1, 2, 3, 4)
            .log()
            .map(i -> i * 2)
            .subscribeOn(Schedulers.parallel())
            .doOnNext(arrList::add)
            .blockLast();

    System.out.println("After: " + arrList);

在这里,您阻塞主线程上的执行,直到处理Flux上的最后一个元素为止。因此,在完全填充ArrayList之前,不会执行最后一个System.out。

请记住,代码在控制台应用程序和Netty等服务器环境中运行的方式有些不同。使控制台应用程序等待所有订阅加入的唯一方法是block

但是在并行线程上不允许阻塞。因此,这种方法在Netty环境中行不通。您的服务器将在那里运行,直到显式关闭为止,因此subscribe就可以了。

但是,在上面的代码段中,您要阻止的不仅是防止应用程序退出,还是在读取已填充的数据之前等待。

对上述代码的改进如下:

    ArrayList<Integer> arrList = new ArrayList<>();
    System.out.println("Before: " + arrList);
    Flux.just(1, 2, 3, 4)
            .log()
            .map(i -> i * 2)
            .subscribeOn(Schedulers.parallel())
            .doOnNext(arrList::add)
            .doOnComplete(() -> System.out.println("After: " + arrList))
    .blockLast();

即使在这里,doOnComplete也会从反应链外部访问数据。为了避免这种情况,您可以像这样在链中收集助焊剂的元素,如下所示:

    System.out.println("Before.");
    Flux.just(1, 2, 3, 4)
            .log()
            .map(i -> i * 2)
            .subscribeOn(Schedulers.parallel())
            .collectList()
            .doOnSuccess(list -> System.out.println("After: " + list))
    .block();

同样,请记住,在Netty中运行时(例如Spring Webflux应用程序),以上代码将以subscribe()结尾。

但是请注意,从Flux到List(或任何Collection)的切换意味着您正在从反应式范式切换到命令式编程。您应该能够在Reactive范本本身内实现任何功能。