在Java并发包中,ForkJoinPool是专为分治任务设计的线程池,它可以将一个大任务拆分成多个小任务并行执行,最后合并所有小任务的结果得到最终输出,非常适合处理计算密集型的可拆分任务。

ForkJoinPool核心概念
ForkJoinPool的核心思想是分治,主要包含几个关键角色:
- ForkJoinPool:负责管理工作线程,接收提交的任务并调度执行
- ForkJoinTask:任务的抽象基类,提供了fork和join方法用于任务拆分和结果合并
- RecursiveTask:ForkJoinTask的子类,用于有返回结果的任务
- RecursiveAction:ForkJoinTask的子类,用于没有返回结果的任务
使用RecursiveTask实现有返回结果的任务分解
下面以计算1到n的累加和为例,演示如何使用RecursiveTask拆分任务。当任务的计算范围大于阈值时,将任务拆分成两个子任务,否则直接计算范围内的和。
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
// 自定义任务类,继承RecursiveTask,泛型为返回结果的类型
class SumTask extends RecursiveTask<Long> {
// 任务计算的起始值
private long start;
// 任务计算的结束值
private long end;
// 拆分任务的阈值,当范围大于这个值时拆分任务
private static final long THRESHOLD = 10000;
public SumTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
// 如果当前任务范围小于等于阈值,直接计算
if (end - start <= THRESHOLD) {
long sum = 0;
for (long i = start; i <= end; i++) {
sum += i;
}
return sum;
}
// 否则拆分任务,计算中间值
long middle = (start + end) / 2;
// 创建左子任务
SumTask leftTask = new SumTask(start, middle);
// 创建右子任务
SumTask rightTask = new SumTask(middle + 1, end);
// 执行左子任务,fork方法会将任务提交到线程池异步执行
leftTask.fork();
// 执行右子任务并获取结果,join方法会阻塞直到任务完成返回结果
Long rightResult = rightTask.compute();
// 获取左子任务的结果
Long leftResult = leftTask.join();
// 合并两个子任务的结果
return leftResult + rightResult;
}
}
public class ForkJoinPoolDemo {
public static void main(String[] args) {
// 创建ForkJoinPool实例,默认使用当前机器的CPU核心数作为并行度
ForkJoinPool pool = new ForkJoinPool();
// 创建计算1到100000累加和的任
SumTask task = new SumTask(1, 100000);
// 提交任务到线程池并获取结果
long result = pool.invoke(task);
System.out.println("1到100000的累加和为:" + result);
// 关闭线程池
pool.shutdown();
}
}
使用RecursiveAction实现无返回结果的任务
如果任务不需要返回结果,比如批量处理文件、打印日志等场景,可以继承RecursiveAction。下面演示一个打印1到n所有数字的任务:
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
class PrintTask extends RecursiveAction {
private long start;
private long end;
private static final long THRESHOLD = 100;
public PrintTask(long start, long end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= THRESHOLD) {
for (long i = start; i <= end; i++) {
System.out.println("当前数字:" + i);
}
} else {
long middle = (start + end) / 2;
PrintTask leftTask = new PrintTask(start, middle);
PrintTask rightTask = new PrintTask(middle + 1, end);
leftTask.fork();
rightTask.compute();
leftTask.join();
}
}
}
public class RecursiveActionDemo {
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
PrintTask task = new PrintTask(1, 500);
pool.invoke(task);
pool.shutdown();
}
}
ForkJoinPool使用注意事项
- 拆分任务的阈值需要根据实际业务场景调整,阈值太小会导致任务过多,线程切换开销大;阈值太大会导致任务拆分不充分,无法利用多核优势
- 避免在compute方法中直接调用
Thread.sleep或者进行阻塞IO操作,会影响线程池的工作窃取机制效率 - ForkJoinPool适合计算密集型任务,不适合IO密集型任务,IO密集型任务建议使用普通线程池
- 提交到ForkJoinPool的任务不要抛出受检异常,如果需要处理异常,可以在compute方法内部捕获处理
工作窃取机制简介
ForkJoinPool采用工作窃取算法提升线程利用率,每个工作线程都有自己的任务队列,当某个线程的任务队列为空时,会从其他线程的任务队列尾部窃取任务执行,减少线程空闲时间,这也是ForkJoinPool能高效处理分治任务的重要原因。
ForkJoinPoolJava并行计算任务分解RecursiveTask修改时间:2026-07-09 09:03:35