我最喜欢Java 8的功能之一是流API。 最终,它消除了代码中的几乎所有循环,并使您可以编写更具表现力和重点的代码。
今天,我意识到它可以用于其他用途:作为ForkJoinPool
一个不错的前端。
问题:执行器样板
假设我们要并行运行许多任务。 没什么好说的,让我们说它们每个都只是打印出执行线程的名称(因此我们可以看到它并行运行)。 我们要在完成所有操作后恢复执行。
如果要使用ExecutorService
并行运行一堆任务,则可能需要执行以下操作:
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 5; i++) {executor.submit(() -> System.out.println(Thread.currentThread()));
}
executor.shutdown();
try {executor.awaitTermination(1, TimeUnit.SECONDS);
} catch (InterruptedException ex) {// TODO handle...
}
现在,这是很多代码! 但是我们可以做得更好。
解决方案:流API
最后,我想到了这个实用程序:
void doInParallelNTimes(int times, Runnable op) {IntStream.range(0, times).parallel().forEach(i -> op.run());
}
可重复使用的一切。 像这样称呼它:
doInParallelNTimes(5, () -> System.out.println(Thread.currentThread()));
做完了
这打印出以下内容。 请注意,它实际上也在使用主线程-因为它仍然被扣为人质,并且在执行完成之前无法恢复。
Thread[main,5,main]
Thread[ForkJoinPool.commonPool-worker-1,5,main]
Thread[main,5,main]
Thread[ForkJoinPool.commonPool-worker-3,5,main]
Thread[ForkJoinPool.commonPool-worker-2,5,main]
另一个例子:并行计算
这是另一个例子。 我们可以使用流API并行处理许多不同的任务,而不必重复执行N次相同的操作。 我们可以创建(“种子”)具有任何集合或值集的流,对其并行执行一个函数,最后汇总结果(集合为一个集合,减少为单个值等)。
让我们看看如何计算前45个斐波纳契数的总和:
public class Tester {public static void main(String[] args) {Stopwatch stopwatch = Stopwatch.createStarted();IntStream.range(1, 45).parallel().map(Tester::fib).sum();System.out.println("Parallel took " + stopwatch.elapsed(MILLISECONDS) + " ms");stopwatch.reset();stopwatch.start();IntStream.range(1, 45).map(Tester::fib).sum();System.out.println("Sequential took " + stopwatch.elapsed(MILLISECONDS) + " ms");}private static int fib(int n) {if (n == 1 || n == 2) {return 1;} else {return fib(n - 1) + fib(n - 2);}}
}
打印输出:
Parallel took 3078 ms
Sequential took 7327 ms
它在一行代码中取得了很多成就。 首先,它创建一个流,其中包含我们要并行运行的所有任务的描述。 然后,它并行调用所有这些函数。 最后,它返回所有这些结果的总和。
并非所有人为。 我可以轻松想象创建具有任意值(包括丰富的Java对象)的流,并对它们执行非平凡的操作。 没关系,编排所有看起来仍然相同的东西。
什么时候做?
我认为这种解决方案在所有情况下都非常有用,当您事先知道负载,并且您希望将执行分叉到多个线程并在它们全部完成后恢复。 我需要一些测试代码,但它可能在许多其他派生/合并或分而治之方案中很好地工作。
显然,如果您想在后台运行某些程序并恢复执行,或者想让后台执行程序长时间运行,则无法使用。
翻译自: https://www.javacodegeeks.com/2015/01/java-8-streams-api-as-friendly-forkjoinpool-facade.html