Java 8并行流don';t看起来实际上是并行工作的

Java 8并行流don';t看起来实际上是并行工作的,java,concurrency,parallel-processing,java-8,Java,Concurrency,Parallel Processing,Java 8,我试图使用Java8的parallelStream()并行执行几个长时间运行的请求(例如web请求)。简化示例: List<Supplier<Result>> myFunctions = Arrays.asList(() -> doWebRequest(), ...) List<Result> results = myFunctions.parallelStream().map(function -> function.get()).collec

我试图使用Java8的parallelStream()并行执行几个长时间运行的请求(例如web请求)。简化示例:

List<Supplier<Result>> myFunctions = Arrays.asList(() -> doWebRequest(), ...)

List<Result> results = myFunctions.parallelStream().map(function -> function.get()).collect(...
List myFunctions=Arrays.asList(()->doWebRequest(),…)
List results=myFunctions.parallelStream().map(函数->函数.get()).collect(。。。
因此,如果有两个函数分别阻塞2秒和3秒,我希望在3秒后得到结果。但是,实际上需要5秒-即函数似乎是按顺序执行的,而不是并行执行的。我做错了什么吗

编辑:这是一个例子。当我希望它是~2000时,所用的时间是~4000毫秒

    long start = System.currentTimeMillis();

    Map<String, Supplier<String>> input = new HashMap<String, Supplier<String>>();

    input.put("1", () -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "a";
    });

    input.put("2", () -> {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "b";
    });

    Map<String, String> results = input.keySet().parallelStream().collect(Collectors.toConcurrentMap(
            key -> key,
            key -> {
                return input.get(key).get();
            }));

    System.out.println("Time: " + (System.currentTimeMillis() - start));

}
long start=System.currentTimeMillis();
映射输入=新的HashMap();
input.put(“1”,()->{
试一试{
《睡眠》(2000年);
}捕捉(中断异常e){
e、 printStackTrace();
}
返回“a”;
});
input.put(“2”,()->{
试一试{
《睡眠》(2000年);
}捕捉(中断异常e){
e、 printStackTrace();
}
返回“b”;
});
映射结果=input.keySet().parallelStream().collect(Collectors.toConcurrentMap(
键->键,
键->{
返回input.get(key.get();
}));
System.out.println(“时间:”+(System.currentTimeMillis()-start));
}
如果迭代entrySet()而不是keySet(),则没有任何区别

编辑:将平行零件更改为以下内容也无济于事:

 Map<String, String> results = input.entrySet().parallelStream().map(entry -> {
            return new ImmutablePair<String, String>(entry.getKey(), entry.getValue().get());
    }).collect(Collectors.toConcurrentMap(Pair::getLeft, Pair::getRight));
Map results=input.entrySet().parallelStream().Map(条目->{
返回新的ImmutablePair(entry.getKey(),entry.getValue().get());
}).collect(Collectors.toConcurrentMap(Pair::getLeft,Pair::getRight));

在并行执行时,分解输入集、创建任务以表示计算的不同部分、跨线程分配操作、等待结果、合并结果等都会产生开销。这超出了实际解决问题的工作。如果要始终使用并行框架将问题分解为一个元素的粒度,对于大多数问题,这些开销将压倒实际计算,并行将导致执行速度变慢。因此,并行框架有一定的自由度来决定分解输入的精细程度,这就是这里发生的情况

在您的例子中,您的输入集太小,无法分解,因此库选择按顺序执行

在你的四核系统上试试这个:比较

IntStream.range(0, 100_000).sum()
vs


在这里,您为它提供了足够的输入,它将有信心通过并行执行获得成功,您可能会在这两个示例之间看到几乎线性的加速。

并行执行时,分解输入集、创建任务以表示计算的不同部分、跨线程分布操作、等待结果、合并结果等都会产生开销。这项工作已经完成如果并行框架总是将问题分解到一个元素的粒度,对于大多数问题,这些开销将压倒实际计算,并行将导致执行速度变慢。因此,并行框架有一定的自由度来决定分解输入的精细程度,而这就是这里发生的事情

在您的例子中,您的输入集太小,无法分解,因此库选择按顺序执行

在你的四核系统上试试这个:比较

IntStream.range(0, 100_000).sum()
vs


在这里,您为它提供了足够的输入,它将有信心通过并行执行赢得胜利。如果您使用负责任的测量方法(例如,JMH microbenchmark线束)进行测量,您可能会看到这两个示例之间几乎呈线性的加速。

什么是
parallelStream()的javadoc
say?我已经看过了,但没有看到任何明显相关的东西。你的代码是在多核硬件上运行的吗?是的,它是四核的。如果它始终精确地占用两个lambda的时间之和,那么它要么是顺序的,要么不是有效的并行的。使用Executor服务,这一切都可以正常工作,这就是我要介绍的除非我能弄清楚这一点,否则很可能最终会使用。但到目前为止,与旧的Java并行系统或Scala相比,我对其可用性印象不深。
parallelStream()的javadoc是什么
say?我已经看过了,但没有看到任何明显相关的东西。你的代码是在多核硬件上运行的吗?是的,它是四核的。如果它始终精确地占用两个lambda的时间之和,那么它要么是顺序的,要么不是有效的并行的。使用Executor服务,这一切都可以正常工作,这就是我要介绍的除非我能弄清楚这一点,否则可能最终会使用它。但到目前为止,与旧的Java并行系统或Scala相比,我对它的可用性印象不深。鉴于我知道我想在这种情况下并行运行,有没有办法强迫它实际运行?如果没有,我觉得这个库不够可靠,无法大量使用。基于关于您的实际问题,您提供的信息很少,我认为您误解了Streams库的实际用途。Streams是关于数据并行的;数据并行问题是CPU限制的,而不是IO限制的。似乎您只是希望同时运行一些基本不相关的IO密集型任务。请使用普通的旧线程pool;您的第一个示例是
ExecutorService.invokeAll()
的理想候选者。我最终使用了ExecutorService。如果Streams能够处理这两种情况就好了(我不明白为什么它不能),因为它的API是