将多个并行对象连接到单个List中

时间:2018-05-11 19:19:02

标签: java concurrency java-stream

我有一个键值地图并迭代键,调用服务并根据响应,我将所有响应添加到某些function intersection(arrayOfArrays) { return arrayOfArrays.reduce((a, b) => a.filter(v => b.includes(v))); } console.log(intersection([[5, 10, 15, 20], [15, 88, 1, 5, 7], [1, 10, 15, 5, 20]]));

如何同时执行不同的操作?将uberList更改为stream()会有效吗?它在添加到parallelStream()时会同步吗?

想法是尽量缩短响应时间。

uberList

4 个答案:

答案 0 :(得分:1)

您可能不希望将parallelStream用于并发,仅用于并行。 (即:将它用于您希望在概念上顺序执行的任务中有效使用多个物理过程的任务,而不是用于您希望在概念上同时进行多项任务的任务。)

在你的情况下,你可能最好使用来自Google Guava的ExecutorService或更具体的com.google.common.util.concurrent.ListenableExecutorService(警告:我还没有尝试编译以下代码,可能会有语法错误):

int MAX_NUMBER_OF_SIMULTANEOUS_REQUESTS = 100;
ListeningExecutorService myExecutor = 
    MoreExecutors.listeningDecorator(
        Executors.newFixedThreadPool(MAX_NUMBER_OF_SIMULTANEOUS_REQUESTS));

List<ListenableFuture<Optional<MyClass>>> futures = new ArrayList<>();
for (Map.Entry<String, List<MyOtherClass>> entry : map.entrySet()) {
  if (entry.getValue().size() > 0 && entry.getValue().values().size() > 0) {
    futures.add(myExecutor.submit(() -> {    
      // Do stuff
      if(noError) {
        return Optional.of(MyClass3);
      } else {
        return Optional.empty();
      }
    }));
  }
}

List<MyClass> uberList = Futures.successfulAsList(futures)
    .get(1, TimeUnit.MINUTES /* adjust as necessary */)
    .stream()
    .filter(Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

此代码的优点在于它允许您明确指定任务应始终在&#34;同一时间开始#34; (至少在概念上)并允许你明确地控制你的并发性(允许多少个同时请求?如果某些任务失败,我们该怎么办?我们愿意等多久?等等)。并行流并不是真的。

答案 1 :(得分:1)

  

如何同时执行不同的操作?

一个线程一次可以完成一个任务。如果你想同时做多个操作,你必须离开其他线程。 您可以创建新的Thread或使用ExecutorService来管理线程池,为任务排队并为您执行任务。

  

将stream()更改为parallelStream()可以解决问题吗?

是的。在内部,parallelStream()使用ForkJoinPool.commonPool()为您运行任务。但请记住,parallelStream()无法保证返回的流是否并行(但目前,当前实现返回并行流)

  

它在添加到uberList时是否同步?

您可以在forEach管道中执行同步部分。通常,您不希望在collection.add()内调用forEach来创建集合。相反,您应该使用.map().collect(toX())方法。它将你从同步部分中解放出来:

  1. 它不需要知道你的局部变量(在这种情况下是uberlist。并且它不会在执行时修改它,有助于减少由并发引起的许多奇怪的错误
  2. 您可以在.collect()部分自由更改收藏类型。它使您可以更好地控制结果类型。
  3. 使用并行流时,它不需要对给定集合进行线程安全或同步。因为“可以实例化,填充和合并多个中间结果,以便保持可变数据结构的隔离”(Read more about this here
  4. 所以你想要的是同时执行多个类似的服务调用并将结果收集到列表中。

    您可以通过并行流来完成:

    uberList = map.entrySet().stream()
                             .parallel()  // Use .stream().parallel() to force parallism. The .parallelStream() does not guarantee that the returned stream is parallel stream
                             .filter(yourCondition)
                             .map(e -> yourService.methodCall(e))
                             .collect(Collectors.toList());
    

    非常酷,不是吗?

    但正如我所说,默认并行流使用ForkJoinPool.commonPool()进行线程排队和执行。

    糟糕的部分是,如果您的yourService.methodCall(e) 执行重IO内容(如HTTP调用,甚至数据库调用...)或长时间运行任务,那么可能会耗尽池,其他传入的任务将永远排队等待执行。

    因此,通常所有其他任务都依赖于此公共池(不仅是您自己的yourService.methodCall(e),而是所有其他并行流)由于排队时间而变慢。

    要解决此问题,您可以在自己的fork-join池上强制执行并行:

    ForkJoinPool forkJoinPool = new ForkJoinPool(4); // Typically set it to Runtime.availableProcessors()
    uberlist = forkJoinPool.submit(() -> {
         return map.entrySet().stream()
                                 .parallel()  // Use .stream().parallel() to force parallism. The .parallelStream() does not guarantee that the returned stream is parallel stream
                                 .filter(yourCondition)
                                 .map(e -> yourService.methodCall(e))
                                 .collect(Collectors.toList());
    }).get();
    

答案 2 :(得分:0)

我相信您可以使用并行流和同步地图和列表来执行您需要的操作。

答案 3 :(得分:0)

Parallel Stream将有助于同时执行。但是不建议做forEach循环并在外部列表中添加元素。如果你这样做,你必须确保同步外部列表。更好的方法是使用map并将结果收集到列表中。在这种情况下,parallelStream负责同步。

List<MyClass> uberList = map.entrySet().parallelStream().filter(s -> 
s.getValue().size() > 0 && s.getValue().values().size() > 
0).map(
y  -> {
    // Do stuff
        return MyClass3;
    }
 }
.filter(t -> check no ertor condition)
.collect (Collectors.toList())