Java 8如何借助库简便实现foreach循环并行化?附自定义方案
Java 8 中 foreach 循环并行化的实用方案
嘿,这个问题问得很实在!Java 8本身就提供了不用手动写多线程的简便并行遍历方案——Stream API的并行流,直接就能搞定你的需求。比如你原来的业务代码,可以改成这样:
public void someFunction() { lotsOfObjects.parallelStream().forEach(this::doSomethingThatCanBeDoneInParallel); }
并行流底层会自动用ForkJoinPool管理线程,默认线程数和CPU核心数挂钩,不用你手动创建、维护线程池,省心又高效。
不过看你补充的场景——代码要每帧运行,频繁创建ExecutorService开销太高,还不能调用shutdown()等待任务完成,这种情况下并行流可能会有一些局限(比如默认的ForkJoinPool是全局共享的,每帧提交大量任务可能会影响其他并行任务的执行)。
那你自己实现的ParallelForI类确实是适配这个特殊场景的好方案,我把你的实现和使用示例整理出来,方便其他有类似需求的开发者参考:
ParallelForI 完整实现代码
import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ParallelForI { // 用静态线程池复用线程,避免频繁创建实例的开销 private static final ExecutorService executor = Executors.newCachedThreadPool(); public static <T> void forEach(List<T> list, int start, int end, ParallelTask<T> task) { if (start >= end) { return; } // 递归拆分任务,分治并行执行 int middle = (start + end) / 2; CountDownLatch latch = new CountDownLatch(2); executor.execute(() -> { try { forEach(list, start, middle, task); } finally { latch.countDown(); } }); executor.execute(() -> { try { forEach(list, middle, end, task); } finally { latch.countDown(); } }); try { // 等待两个子任务完成,不用shutdown线程池 latch.await(); } catch (InterruptedException e) { // 恢复中断状态 Thread.currentThread().interrupt(); } } public static <T> void forEach(List<T> list, ParallelTask<T> task) { if (list == null || list.isEmpty()) { return; } forEach(list, 0, list.size(), task); } // 定义并行任务的函数式接口 public interface ParallelTask<T> { void run(T element, int index); } }
使用示例
public void someFunction() { ParallelForI.forEach(lotsOfObjects, (obj, index) -> { doSomethingThatCanBeDoneInParallel(obj); }); }
这个实现用了分治思想递归拆分任务,搭配CountDownLatch等待子任务完成,而且用静态的线程池复用线程,完美解决了你不能关闭线程池、频繁创建开销高的问题。如果你的场景需要控制线程数量,也可以把newCachedThreadPool()换成newFixedThreadPool(n),根据业务需求设置固定线程数,减少线程上下文切换的开销。
内容的提问来源于stack exchange,提问作者Shuang Li




