进一步并行化两个Java parallelStream()的计算

时间:2016-01-12 10:23:20

标签: java concurrency java-8 java-stream

我有一个方法可以并行计算列表中数字的平方并将它们相加:

public static double sumSquared(List<Double> values) {
    return values
            .parallelStream()
            .mapToDouble(d -> d * d)
            .sum();
}

假设存在一些双重列表,例如,如何进一步并行化,例如

double result = sumSquared(list0) + (sumSquared(list1) * sumSquared(list2));

有优雅的方式吗?我认为为每个列表操作启动单独的线程并没有多大帮助,因为它们使用相同的线程池。它是否有意义或是否应如上所述计算结果?

1 个答案:

答案 0 :(得分:5)

问题确实是即使sumSquared()并行处理数据,每个sumSquared()调用都是按顺序执行的。因此,这些列表仍然是逐个处理的,因此如果这些列表中的一些列表太小而无法分散到多个CPU中,则会丢失CPU处理时间。

要避免这种情况,请使用Java 8中引入的新CompletionStage / CompletableFuture并行运行整个计算:

// each sumSquared() is submitted immediately to the common pool
CompletionStage<Double> sumList0 = CompletableFuture.supplyAsync(() -> sumSquared(list0));
CompletionStage<Double> sumList1 = CompletableFuture.supplyAsync(() -> sumSquared(list1));
CompletionStage<Double> sumList2 = CompletableFuture.supplyAsync(() -> sumSquared(list2));

// as soon as sumList1 and sumList2 have both complete, their product is computed
CompletionStage<Double> prodSumList1and2 = sumList1.thenCombine(sumList2, (a, b) -> a * b);
// as soon as their product is computed and sumList0 is finished, the final sum is computed
CompletionStage<Double> result = sumList0.thenCombine(prodSumList1and2, Double::sum);

System.out.println(result.toCompletableFuture().get());

请注意,这使用公共池,默认情况下,该池使用所有CPU。因此,如果您只执行CPU繁重的操作,则使用自定义池可能无法提高性能,除非您有其他并行运行的作业。

如果您的处理更多是I / O绑定或内存绑定,那么情况可能会有所不同。