Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/398.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java并行流在集合上无法正常工作_Java_Multithreading_Concurrency_Java Stream - Fatal编程技术网

Java并行流在集合上无法正常工作

Java并行流在集合上无法正常工作,java,multithreading,concurrency,java-stream,Java,Multithreading,Concurrency,Java Stream,有一点背景知识,我尝试使用Java8并行流以异步方式调用多个API。我希望调用每个API,然后阻塞,直到返回所有API。我遇到了一个有趣的情况,如果我尝试流化一个映射,而不是一个列表,那么在新线程中不再调用API 如果运行以下代码,将在新线程中调用每个服务: List<GitUser> result1 = Arrays.asList(service1, service2, service3).parallelStream() .map(s->s.getG

有一点背景知识,我尝试使用Java8并行流以异步方式调用多个API。我希望调用每个API,然后阻塞,直到返回所有API。我遇到了一个有趣的情况,如果我尝试流化一个映射,而不是一个列表,那么在新线程中不再调用API

如果运行以下代码,将在新线程中调用每个服务:

    List<GitUser> result1 = Arrays.asList(service1, service2, service3).parallelStream()
        .map(s->s.getGitUser())
        .collect(Collectors.toList());

注意,Javadoc声明 “返回一个可能的并行流,并将此集合作为其源。此方法允许返回一个连续的流。”。流中的元素数量似乎太少,无法分解为多个线程执行的组件,因此您的代码是按顺序执行的,而不是并行执行的。仅仅因为您正在使用,并不一定意味着它将并行执行,这完全取决于库确定什么足以并行化或不足以获得最佳性能


除了Joe C在评论中提到的上述内容之外,您还需要大量增加流源代码中的元素数量,以便实际看到性能方面的任何影响。

请注意,java文档声明 “返回一个可能的并行流,并将此集合作为其源。此方法允许返回一个连续的流。”。流中的元素数量似乎太少,无法分解为多个线程执行的组件,因此您的代码是按顺序执行的,而不是并行执行的。仅仅因为您正在使用,并不一定意味着它将并行执行,这完全取决于库确定什么足以并行化或不足以获得最佳性能

除了Joe C在评论中所述的上述内容之外,您还需要大量增加流源中的元素数量,以便实际看到性能方面的任何影响。

如中所述,这是关于如何分配工作负载的实现细节
HashMap
有一个内部备份数组,其容量比条目(通常)高。它根据数组元素进行拆分,知道这可能会产生不平衡拆分,因为确定条目在数组中的分布方式可能代价高昂

最简单的解决方案是,当您知道只有几个元素时,减少
哈希集的容量(默认容量为16):

HashMap<Integer,String> map = new HashMap<>();
map.put(0, "foo");
map.put(1, "bar");
map.put(2, "baz");

map.values().parallelStream().forEach(v -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    System.out.println(v+"\t"+Thread.currentThread());
});
请注意,由于舍入问题,它仍然没有为每个元素使用一个线程。如上所述,
HashMap
Spliterator
不知道元素如何分布在数组中。但它知道总共有三个元素,所以它估计在拆分后的每个工作负载中有一半的元素。三分之二被四舍五入为一,因此
实现假设即使尝试进一步细分这些工作负载也没有好处

除了使用具有更多元素的并行流之外,没有简单的解决方法。不过,仅出于教育目的:

HashMap<Integer,String> map = new HashMap<>(4, 1f);
map.put(0, "foo");
map.put(1, "bar");
map.put(2, "baz");
map.put(3, null);

map.values().parallelStream()
   .filter(Objects::nonNull)
   .forEach(v -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    System.out.println(v+"\t"+Thread.currentThread());
});
通过插入第四个元素,我们消除了舍入问题。它还需要提供
1f
的负载系数,以防止
HashMap
增加容量,从而使我们回到原点(除非我们至少有八个内核)

这是一个难题,正如我们预先知道的,我们将浪费一个工作线程来检测我们的伪
null
条目。但它演示了工作负载分割是如何工作的

地图中有更多的元素会自动消除这些问题


流不是为执行阻塞或休眠的任务而设计的。对于这类任务,您应该使用
ExecutorService
,它还允许使用比CPU核更多的线程,这对于在整个执行时间内不使用CPU核的任务来说是合理的

ExecutorService es = Executors.newCachedThreadPool();
List<GitUser> result =
    es.invokeAll(
        Stream.of(service1, service2, service3)
              .<Callable<GitUser>>map(s -> s::getGitUser)
              .collect(Collectors.toList())
    ) .stream()
      .map(future -> {
            try { return future.get(); }
            catch (InterruptedException|ExecutionException ex) {
                throw new IllegalStateException(ex);
            }
        })
      .collect(Collectors.toList());
Executors服务=Executors.newCachedThreadPool();
列表结果=
艾尔(
流(服务1、服务2、服务3)
.map(s->s::getGitUser)
.collect(收集器.toList())
).stream()
.map(未来->{
尝试{return future.get();}
捕获(InterruptedException | ExecutionException ex){
抛出新的非法状态异常(ex);
}
})
.collect(Collectors.toList());
如中所述,这是关于如何拆分工作负载的实现细节
HashMap
有一个内部备份数组,其容量比条目(通常)高。它根据数组元素进行拆分,知道这可能会产生不平衡拆分,因为确定条目在数组中的分布方式可能代价高昂

最简单的解决方案是,当您知道只有几个元素时,减少
哈希集的容量(默认容量为16):

HashMap<Integer,String> map = new HashMap<>();
map.put(0, "foo");
map.put(1, "bar");
map.put(2, "baz");

map.values().parallelStream().forEach(v -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    System.out.println(v+"\t"+Thread.currentThread());
});
请注意,由于舍入问题,它仍然没有为每个元素使用一个线程。如上所述,
HashMap
Spliterator
不知道元素如何分布在数组中。但它知道总共有三个元素,所以它估计在拆分后的每个工作负载中有一半的元素。三分之二被四舍五入为一,因此
实现假设即使尝试进一步细分这些工作负载也没有好处

除了使用具有更多元素的并行流之外,没有简单的解决方法。不过,仅出于教育目的:

HashMap<Integer,String> map = new HashMap<>(4, 1f);
map.put(0, "foo");
map.put(1, "bar");
map.put(2, "baz");
map.put(3, null);

map.values().parallelStream()
   .filter(Objects::nonNull)
   .forEach(v -> {
    LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    System.out.println(v+"\t"+Thread.currentThread());
});
通过插入第四个元素,我们消除了舍入问题。还需要提供
1fExecutorService es = Executors.newCachedThreadPool();
List<GitUser> result =
    es.invokeAll(
        Stream.of(service1, service2, service3)
              .<Callable<GitUser>>map(s -> s::getGitUser)
              .collect(Collectors.toList())
    ) .stream()
      .map(future -> {
            try { return future.get(); }
            catch (InterruptedException|ExecutionException ex) {
                throw new IllegalStateException(ex);
            }
        })
      .collect(Collectors.toList());