我正在尝试使用Java 8的parallelStream()并行执行多个长时间运行的请求(例如Web请求)。简化示例:
List<Supplier<Result>> myFunctions = Arrays.asList(() -> doWebRequest(), ...)
List<Result> results = myFunctions.parallelStream().map(function -> function.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));
}
如果我遍历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));
答案 0 :(得分:9)
并行执行时,会产生分解输入集的开销,创建表示计算的不同部分的任务,跨线程分配操作,等待结果,组合结果等。这超出了工作范围。实际上解决问题。如果并行框架总是将问题分解为一个元素的粒度,对于大多数问题,这些开销将压倒实际计算,并行性将导致执行速度变慢。因此,并行框架有一定的自由度来决定分解输入的精确程度,以及这里发生的事情。
在您的情况下,您的输入集太小而无法分解。因此库选择按顺序执行。
在您的四核系统上试试这个:比较
IntStream.range(0, 100_000).sum()
VS
IntStream.range(0, 100_000).parallel().sum()
在这里,你给它足够的输入,它将确信它可以通过并行执行获胜。如果您使用负责任的测量方法(例如,JMH微基准线束)进行测量,您可能会在这两个示例之间看到几乎线性的加速。