Java的ForkJoin框架是JDK7引入的用于并行执行任务的框架,核心思想是将大任务拆分成多个小任务并行处理,最后合并所有小任务的结果得到最终结果,非常适合处理可以递归拆分的并行计算场景,数组求和就是典型的应用场景。

ForkJoin框架核心概念
ForkJoin框架主要包含三个核心部分:
- ForkJoinPool:任务执行线程池,管理工作线程,负责接收任务并调度执行
- ForkJoinTask:任务抽象类,提供了fork和join方法,常用子类有RecursiveTask(有返回值)和RecursiveAction(无返回值)
- 工作窃取算法:空闲的工作线程会从其他线程的任务队列尾部窃取任务执行,减少线程空闲时间,提升CPU利用率
数组并行求和实现步骤
1. 自定义求和任务类
继承RecursiveTask
import java.util.concurrent.RecursiveTask;
public class ArraySumTask extends RecursiveTask<Long> {
// 拆分的阈值,数组长度小于等于该值时直接计算,不再拆分
private static final int THRESHOLD = 10000;
private final long[] array;
private final int start;
private final int end;
public ArraySumTask(long[] array, int start, int end) {
this.array = array;
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 当任务范围小于等于阈值时,直接计算求和
if (end - start <= THRESHOLD) {
long sum = 0;
for (int i = start; i < end; i++) {
sum += array[i];
}
return sum;
}
// 任务范围过大,拆分成两个子任务
int mid = (start + end) / 2;
ArraySumTask leftTask = new ArraySumTask(array, start, mid);
ArraySumTask rightTask = new ArraySumTask(array, mid, end);
// 执行子任务
leftTask.fork();
rightTask.fork();
// 合并子任务结果
return leftTask.join() + rightTask.join();
}
}
2. 编写测试代码
创建ForkJoinPool实例,提交自定义的任务,等待结果返回,同时可以对比单线程求和的性能差异。
import java.util.concurrent.ForkJoinPool;
public class ForkJoinSumTest {
public static void main(String[] args) {
// 生成长度为100万的测试数组
int length = 1000000;
long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = i + 1;
}
// 使用ForkJoin并行求和
ForkJoinPool pool = new ForkJoinPool();
ArraySumTask task = new ArraySumTask(array, 0, array.length);
long startTime = System.currentTimeMillis();
long forkJoinResult = pool.invoke(task);
long forkJoinTime = System.currentTimeMillis() - startTime;
System.out.println("ForkJoin并行求和结果:" + forkJoinResult + ",耗时:" + forkJoinTime + "毫秒");
// 单线程求和对比
startTime = System.currentTimeMillis();
long singleThreadResult = 0;
for (long num : array) {
singleThreadResult += num;
}
long singleThreadTime = System.currentTimeMillis() - startTime;
System.out.println("单线程求和结果:" + singleThreadResult + ",耗时:" + singleThreadTime + "毫秒");
}
}
关键注意事项
阈值选择
拆分阈值需要根据实际场景调整,如果阈值设置过大,任务拆分不够细,无法充分利用多核资源;如果阈值设置过小,会产生大量小任务,增加任务调度的开销。一般可以根据数组大小和CPU核心数调整,比如CPU是8核,可以将阈值设置为数组长度的1/8左右,再结合实际测试优化。
避免任务拆分过深
递归拆分任务时如果层级过深,会导致栈溢出,因此需要合理设置阈值,保证拆分层级不会过多。如果数组长度非常大,也可以考虑先按照CPU核心数拆分初始任务,再让每个初始任务内部处理一部分数组范围,减少递归层级。
异常处理
ForkJoinTask执行过程中抛出的异常不会直接抛出,需要通过getException()方法获取,或者使用invoke()方法时捕获ExecutionException来处理任务执行中的异常。
适用场景
ForkJoin框架适合处理可以递归拆分的CPU密集型并行任务,比如大规模数组排序、矩阵运算、文件内容并行处理等场景。如果是IO密集型任务,或者使用线程池就可以简单实现的场景,不需要使用ForkJoin,避免增加代码复杂度。
ForkJoinJava并行计算数组求和RecursiveTask修改时间:2026-07-09 06:24:10