You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

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

火山引擎 最新活动