Java 8 Streams API作为友好的ForkJoinPool外观

我最喜欢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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值