简体   繁体   English

Java 8并行流似乎实际上不是并行工作的

[英]Java 8 parallel streams don't appear to actually be working in parallel

I'm trying to use Java 8's parallelStream() to execute several long-running requests (eg web requests) in parallel. 我正在尝试使用Java 8的parallelStream()并行执行几个长时间运行的请求(例如Web请求)。 Simplified example: 简化示例:

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

List<Result> results = myFunctions.parallelStream().map(function -> function.get()).collect(...

So if there are two functions that block for 2 and 3 seconds respectively, I'd expect to get the result after 3 seconds. 因此,如果有两个函数分别阻塞2秒钟和3秒钟,那么我希望3秒钟后得到结果。 However, it really takes 5 seconds - ie it seems the functions are being executed in sequence and not in parallel. 但是,实际上需要5秒钟-即似乎函数是按顺序而不是并行执行的。 Am I doing something wrong? 难道我做错了什么?

edit: This is an example. 编辑:这是一个例子。 The time taken is ~4000 milliseconds when I want it to be ~2000. 当我希望它是〜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));

}

Doesn't make any difference if I iterate over the entrySet() instead of the keySet() 如果我遍历entrySet()而不是keySet()并没有什么区别

edit: changing the parallel part to the following also does not help: 编辑:将并行部分更改为以下内容也无济于事:

 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));

When executing in parallel, there is overhead of decomposing the input set, creating tasks to represent the different portions of the calculation, distributing the actions across threads, waiting for results, combining results, etc. This is over and above the work of actually solving the problem. 并行执行时,需要分解输入集,创建代表计算的不同部分的任务,在线程之间分配动作,等待结果,组合结果等开销,这超出了实际解决的工作问题。 If a parallel framework were to always decompose problems down to a granularity of one element, for most problems, these overheads would overwhelm the actual computation and parallelism would result in a slower execution. 如果并行框架始终将问题分解为一个元素的粒度,那么对于大多数问题,这些开销将使实际计算不堪重负,而并行性将导致执行速度变慢。 So parallel frameworks have some latitude to decide how finely to decompose the input, and that's what's happening here. 因此,并行框架有一定的自由度来决定分解输入的精细程度,这就是这里发生的事情。

In your case, your input set is simply too small to be decomposed. 在您的情况下,您的输入集太小而无法分解。 So the library chooses to execute sequentially. 因此,库选择按顺序执行。

Try this on your four-core system: compare 在您的四核系统上尝试:比较

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

vs

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

Here, you're giving it enough input that it will be confident it can win through parallel execution. 在这里,您将为其提供足够的输入,使其有信心可以通过并行执行获胜。 If you measure with a responsible measurement methodology (say, the JMH microbenchmark harness), you'll probably see an almost-linear speedup between these two examples. 如果使用负责任的测量方法(例如,JMH微基准测试工具)进行测量,那么您可能会发现这两个示例之间的线性加速几乎是线性的。

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

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