Fork/Join解决了传统线程池的俩个大问题:一是无法对大任务进行拆分,对于某个任务只能由单线程执行;二是工作线程从队列中获取任务时存在竞争情况。
Fork/Join是什么
- 传统线程池ThreadPoolExecutor有两个明显的缺点:一是无法对大任务进行拆分,对于某个任务只能由单线程执行;二是工作线程从队列中获取任务时存在竞争情况。
- 这两个缺点都会影响任务的执行效率。为了解决传统线程池的缺陷,Java7中引入Fork/Join框架,并在Java8中得到广泛应用。
- ForkJoinPool是ThreadPoolExecutor线程池的一种补充,是对计算密集型场景的加强。
- Fork/Join框架的核心是ForkJoinPool类,它是对AbstractExecutorService类的扩展。
- ForkJoinPool允许其他线程向它提交任务,并根据设定将这些任务拆分为粒度更细的子任务,这些子任务将由ForkJoinPool内部的工作线程来并行执行,并且工作线程之间可以窃取彼此之间的任务。
Fork/Join的适用场景
- 最适合计算密集型任务。
- 最好是非阻塞任务。
- 任务总数、单任务执行耗时以及并行数都会影响到Fork/Join的性能。
Fork/Join的内容
- 分治任务的线程池:ForkJoinPool
- 分治任务:ForkJoinTask
分治任务的线程池:ForkJoinPool
- ForkJoinPool 是用于执行 ForkJoinTask 任务的执行池,不再是传统执行池 Worker+Queue 的组合式,而是维护了一个队列数组 WorkQueue(WorkQueue[]),这样在提交任务和线程任务的时候大幅度减少碰撞。
- ForkJoinPool中的任务执行可以去窃取其他线程的任务。
ForkJoinPool的构造方法

- ForkJoinPool中有四个核心参数,用于控制线程池的并行数、工作线程的创建、异常处理和模式指定等。各参数解释如下:
- int parallelism:指定并行级别(parallelism level)。ForkJoinPool将根据这个设定,决定工作线程的数量。如果未设置的话,将使用Runtime.getRuntime().availableProcessors()来设置并行级别;
- ForkJoinWorkerThreadFactory factory:ForkJoinPool在创建线程时,会通过factory来创建。注意,这里需要实现的是ForkJoinWorkerThreadFactory,而不是ThreadFactory。如果你不指定factory,那么将由默认的DefaultForkJoinWorkerThreadFactory负责线程的创建工作;
- UncaughtExceptionHandler handler:指定异常处理器,当任务在运行中出错时,将由设定的处理器处理;
- boolean asyncMode:设置队列的工作模式:asyncMode ? FIFO_QUEUE : LIFO_QUEUE。当asyncMode为true时,将使用先进先出队列,而为false时则使用后进先出的模式。
ForkJoinPool的提交任务方式
- execute方法:在提交任务后,不会返回结果。ForkJoinPool不仅允许提交ForkJoinTask类型任务,还允许提交Runnable任务。执行Runnable类型任务时,将会转换为ForkJoinTask类型。由于任务是不可切分的,所以这类任务无法获得任务拆分这方面的效益,不过仍然可以获得任务窃取带来的好处和性能提升。
- invoke方法:接受ForkJoinTask类型的任务,并在任务执行结束后,返回泛型结果。如果提交的任务是null,将抛出空指针异常。
- submit方法:支持三种类型的任务提交:ForkJoinTask类型、Callable类型和Runnable类型。在提交任务后,将返回ForkJoinTask类型的结果。如果提交的任务是null,将抛出空指针异常,并且当任务不能按计划执行的话,将抛出任务拒绝异常。
分治任务:ForkJoinTask
- ForkJoinTask是ForkJoinPool的核心之一,它是任务的实际载体,定义了任务执行时的具体逻辑和拆分逻辑。
- ForkJoinTask继承了Future接口,所以也可以将其看作是轻量级的Future。
- ForkJoinTask 是一个抽象类,它的方法有很多,最核心的是 fork() 方法和 join() 方法,承载着主要的任务协调作用,一个用于任务提交,一个用于结果获取。
- 提交任务fork():用于向当前任务所运行的线程池中提交任务。如果当前线程是ForkJoinWorkerThread类型,将会放入该线程的工作队列,否则放入common线程池的工作队列中。
- 获取任务执行结果join():用于获取任务的执行结果。调用join()时,将阻塞当前线程直到对应的子任务完成运行并返回结果。
ForkJoinTask的子类
- 通常情况下我们不需要直接继承ForkJoinTask类,而只需要继承它的子类,Fork/Join框架提供了以下三个子类:
- RecursiveAction:用于递归执行但不需要返回结果的任务。
- RecursiveTask:用于递归执行需要返回结果的任务。
- CountedCompleter :在任务完成执行后会触发执行一个自定义的钩子函数
Fork/Join的实现原理
- ForkJoinPool内部有多个工作队列,当我们通过 ForkJoinPool 的 invoke() 或者 submit() 方法提交任务时,ForkJoinPool 根据一定的路由规则把任务提交到一个工作队列中,如果任务在执行过程中会创建出子任务,那么子任务会提交到工作线程对应的工作队列中。
- ForkJoinPool 的每个工作线程都维护着一个工作队列(WorkQueue),这是一个双端队列(Deque),里面存放的对象是任务(ForkJoinTask)。
- 每个工作线程在运行中产生新的任务(通常是因为调用了 fork())时,会放入工作队列的top,并且工作线程在处理自己的工作队列时,使用的是 LIFO 方式,也就是说每次从top取出任务来执行。
- 每个工作线程在处理自己的工作队列同时,会尝试窃取一个任务,窃取的任务位于其他线程的工作队列的base,也就是说工作线程在窃取其他工作线程的任务时,使用的是FIFO 方式。
- 在遇到 join() 时,如果需要 join 的任务尚未完成,则会先处理其他任务,并等待其完成。
- 在既没有自己的任务,也没有可以窃取的任务时,进入休眠 。
Fork/Join的使用方式
import java.time.Duration;
import java.time.Instant;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.IntStream;
public class ForkJoinTest {
static final int NCPU = Runtime.getRuntime().availableProcessors();
static long calcSum;
public static void main(String[] args) throws Exception {
int[] array = buildRandomIntArray(200000000);
System.out.println("=============单线程任务开始执行=============");
Instant now = Instant.now();
calcSum = seqSum(array);
System.out.println("执行的结果为:" + calcSum + " 执行时间为:" + Duration.between(now, Instant.now()).toMillis());
System.out.println("=============单线程任务结束执行=============");
System.out.println("\n");
LongSum ls = new LongSum(array, 0, array.length);
ForkJoinPool fjp = new ForkJoinPool(NCPU);
System.out.println("=============forkjoin任务开始执行=============");
now = Instant.now();
ForkJoinTask<Long> result = fjp.submit(ls);
System.out.println("执行的结果为:" + result.get() + " 执行时间为:" + Duration.between(now, Instant.now()).toMillis());
System.out.println("=============forkjoin任务结束执行=============");
System.out.println("\n");
fjp.shutdown();
System.out.println("=============并行流计算任务开始执行=============");
now = Instant.now();
Long sum = (Long) IntStream.of(array).asLongStream().parallel().sum();
System.out.println("执行的结果为:" + sum + " 执行时间为:" + Duration.between(now, Instant.now()).toMillis());
System.out.println("=============并行流计算任务结束执行=============");
}
static long seqSum(int[] array) {
long sum = 0;
for (int i = 0; i < array.length; ++i) {
sum += array[i];
}
return sum;
}
public static int[] buildRandomIntArray(final int size) {
int[] resultArray = new int[size];
Random generator = new Random();
for (int i = 0; i < resultArray.length; i++) {
resultArray[i] = generator.nextInt(1000);
}
return resultArray;
}
}
class LongSum extends RecursiveTask<Long> {
static final int SEQUENTIAL_THRESHOLD = 100000000;
int low;
int high;
int[] array;
LongSum(int[] arr, int lo, int hi) {
array = arr;
low = lo;
high = hi;
}
@Override
protected Long compute() {
if (high - low <= SEQUENTIAL_THRESHOLD) {
long sum = 0;
for (int i = low; i < high; ++i) {
sum += array[i];
}
return sum;
} else {
int mid = low + (high - low) / 2;
LongSum left = new LongSum(array, low, mid);
LongSum right = new LongSum(array, mid, high);
left.fork();
right.fork();
long rightAns = right.compute();
long leftAns = left.join();
return leftAns + rightAns;
}
}
}

结束语
- 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL有非常深入的了解
- 关注公众号,每天持续高效的了解并发编程!
- 关注公众号,后续持续高效的了解spring源码!
- 这个公众号,无广告!!!每日更新!!!

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具