简体   繁体   English

是否可以使用 Java 8 Streams API 进行异步处理?

[英]Is it possible to use Java 8 Streams API for asynchronous processing?

I've been playing with CompletionStage/CompletableFuture in Java 8 in order to do asynchronous processing, which works quite well.我一直在 Java 8 中使用 CompletionStage/CompletableFuture 来进行异步处理,效果很好。 However, sometimes I want a stage to perform asynchronous processing of an iterator/stream of items, and there doesn't seem to be a way to do this.然而,有时我想要一个阶段来执行迭代器/项目流的异步处理,但似乎没有办法做到这一点。

Specifically, Stream.forEach() has the semantics that after the call all items have been processed.具体来说,Stream.forEach() 具有在调用之后处理所有项目的语义。 I would want the same thing, but with a CompletionStage instead, eg:我想要同样的东西,但是用 CompletionStage 代替,例如:

CompletionStage<Void> done = stream.forEach(...);
done.thenRun(...);

If the Stream is backed by an asynchronous streaming result this would be much better than waiting for it to be complete in the above code itself.如果 Stream 由异步流结果支持,这将比在上面的代码本身中等待它完成要好得多。

Is it possible to construct this with current Java 8 API somehow?是否可以用当前的 Java 8 API 以某种方式构建它? Workarounds?解决方法?

As far as I know, the streams API does not support asynchronous event processing. 据我所知,流API不支持异步事件处理。 Sounds like you want something like Reactive Extensions for .NET, and there is a Java port of it called RxJava , created by Netflix. 听起来好像您想要类似.NET的Reactive Extensions,并且它有一个Java端口,称为RXJava ,由Netflix创建。

RxJava supports many of the same high-level operations as Java 8 streams (such as map and filter) and is asynchronous. RxJava支持许多与Java 8流相同的高级操作(例如,映射和过滤器),并且是异步的。

Update : There is now a reactive streams initiative in the works, and it looks like JDK 9 will include support for at least part of it though the Flow class. 更新 :现在正在工作中有一个反应式流倡议,并且看起来JDK 9将通过Flow类包括对它的至少一部分的支持。

As @KarolKrol alluded to you can do it with a stream of CompletableFuture . 正如@KarolKrol所暗示的,您可以使用CompletableFuture流来实现。

There is a library that builds on top of JDK8 streams to facilitate working with streams of CompletableFuture called cyclops-react . 有一个库在JDK8流的基础上构建,以方便使用称为cyclops-reactCompletableFuture流。

To compose your streams you can use cyclops-react's fluent promise ike API or you can use simple-react's Stage s . 要组成您的流,可以使用cyclops-react的流畅的promike ike API或使用simple-react的Stage

cyclops-react (I am the author of this library), provides a StreamUtils class for processing Streams. cyclops-react (我是这个库的作者),提供了一个StreamUtils类来处理Streams。 One of the functions it provides is futureOperations, that provides access to the standard Stream terminal operations (and then some) with a twist - the Stream is executed asynchronously and the result is returned inside a CompletableFuture. 它提供的功能之一是futureOperations,它可以通过扭曲方式访问标准的Stream终端操作(然后是某些操作)-Stream异步执行,结果返回到CompletableFuture内部。 .eg 。例如

 Stream<Integer> stream = Stream.of(1,2,3,4,5,6)
                                       .map(i->i+2);
 CompletableFuture<List<Integer>> asyncResult =  StreamUtils.futureOperations(stream,
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

There is also a convience class ReactiveSeq that wraps Stream and provides the same functionality, with a nice fluent API 还有一个方便的类ReactiveSeq,它包装了Stream并提供了相同的功能,并且具有很好的流利API

 CompletableFuture<List<Integer>> asyncResult = ReactiveSeq.of(1,2,3,4,5,6)
                                       .map(i->i+2)
                                       .futureOperations(
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

As Adam has pointed out cyclops-react FutureStreams are designed to process data asynchronously (by mixing Futures and Streams together) - it is particularly suited for multi-threaded operations that involve blocking I/O (such as reading files, making db calls, making rest calls etc). 正如Adam指出的那样, 独眼巨人反应的 FutureStreams设计为异步处理数据(通过将Futures和Streams混合在一起)-特别适合涉及阻塞I / O的多线程操作(例如读取文件,进行db调用,休息电话等)。

It is possible to produce a stream, map each element to CompletionStage , and collect the results using CompletionStage.thenCombine() , but the resulting code will not be more readable then using simple for like this. 可以生成一个流,将每个元素映射到CompletionStage ,然后使用CompletionStage.thenCombine()收集结果,但是像这样简单地使用简单代码,结果代码将不那么可读。

    CompletionStage<Collection<Result>> collectionStage =
        CompletableFuture.completedFuture(
            new LinkedList<>()
        );

    for (Request request : requests) {
        CompletionStage<Result> resultStage = performRequest(request);
        collectionStage = collectionStage.thenCombine(
            resultStage,
            (collection, result) -> {
                collection.add(result);
                return collection;
            }
        );
    }

    return collectionStage;

This example may be easily transformed to functional forEach not loosing readability. 该示例可以很容易地转换为功能,而不会失去可读性。 But using stream's reduce or collect requires additional not so fine code. 但是使用流的reducecollect需要额外的不太好的代码。

Update: CompletableFuture.allOf and CompletableFuture.join provide another, more readable way of transforming collection of future results to future collection of results. 更新: CompletableFuture.allOfCompletableFuture.join提供了另一种更易读的方式,将未来结果的集合转换为未来结果的集合。

As mentioned, use reactive programming: 如前所述,使用反应式编程:

Maven: Maven:
groupId: org.projectreactor groupId:org.projectreactor
artifactId: reactor-spring artifactId:反应器弹簧

Example: 例:

Flux.fromIterable(Arrays.asList("een", "twee"))
    .flatMap(string -> translateDutchToEnglish(string))
    .parallel()
    .runOn(Schedulers.parallel())
    .sequential()
    .collectList()
    .block();

What it does, is create a Flux, which is a set of resolvable items, on which you perform an action: flatMap with callback. 它的作用是创建一个Flux,该Flux是一组可解决的项目,在该项目上您可以执行操作:带有回调的flatMap。
Then, you use the parallel() method to make it asynchronous. 然后,使用parallel()方法使其异步。 runOn() is used to indicate on which thread to run it. runOn()用于指示在哪个线程上运行它。 Schedulers.parallel() indicates use as many threads as there are cpu cores. Schedulers.parallel()表示使用的线程数与CPU内核数一样多。
sequential() brings it back to synchronous to let collectList() collect all results in one list again. sequence()将其恢复为同步状态,以使collectList()再次将所有结果收集到一个列表中。
To wait for all threads to be done with their business, use block(). 要等待所有线程完成其业务,请使用block()。 The result is a list of with the results. 结果是带有结果的列表。

Yes, it is possible using the standard API:是的,可以使用标准 API:

public class BufferedAsyncStreamable<T> implements Iterator<T> {

  @SuppressWarnings("unchecked")
  private final T END_MARKER = (T) new Object();

  private final BlockingQueue<T> queue;
  private T currentValue;

  public BufferedAsyncStreamable(int capacity) {
    queue = new ArrayBlockingQueue<>(capacity);
  }

  public void add(T obj) {
    queue.add(obj);
  }

  public void end() {
    queue.add(END_MARKER);
  }

  @Override
  public boolean hasNext() {
    try {
      return (currentValue = queue.take()) != END_MARKER;
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public T next() {
    return currentValue;
  }

  public Stream<T> stream() {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false);
  }
}

Example usage:用法示例:

var buffer = new BufferedAsyncStreamable<String>(10);

new Thread(() -> {
  var input = new Scanner(System.in);
  for (String line; !"end".equals(line = input.nextLine());) {
    buffer.add(line);
  }
  buffer.end();
}).start();

buffer.stream()
    .map(String::toUpperCase)
    .forEach(System.err::println);

Reads lines from stdin (until "end" ) and converts them to uppercase as they come in.从 stdin 读取行(直到"end" )并在它们进入时将它们转换为大写。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM