ForkJoin框架是Java 7引入的专为递归分治任务设计的并行计算框架,基于工作窃取算法,由ForkJoinPool和ForkJoinTask组成,适用于归并排序、数组求和等计算密集型场景。

ForkJoin框架是Java 7引入的、专为递归式分治任务设计的并行计算框架,核心思想是“分而治之”——把大任务拆成小任务并行执行,再合并结果。它不是通用线程池,而是针对工作窃取(Work-Stealing)优化的轻量级并行工具,特别适合计算密集型、可分解的递归问题,比如归并排序、快速排序、树遍历、大规模数组求和等。
核心组件:ForkJoinPool + ForkJoinTask
ForkJoinPool 是专为 ForkJoinTask 设计的线程池,内部采用工作窃取算法:每个线程维护自己的双端队列(Deque),任务 fork 出来的新子任务压入队列头部;当本队列空了,就从其他线程队列尾部“偷”任务执行——这大幅减少线程竞争,提升 CPU 利用率。
ForkJoinTask 是抽象基类,实际常用两个子类:
- RecursiveAction:无返回值的任务(如遍历、修改数组)
-
RecursiveTask
:有返回值的任务(如求和、找最大值),需重写 compute() 方法
关键操作:fork()、join()、invoke() 的区别
在 compute() 方法中:
立即学习“Java免费学习笔记(深入)”;
本文档主要讲述的是用Apache Spark进行大数据处理——第一部分:入门介绍;Apache Spark是一个围绕速度、易用性和复杂分析构建的大数据处理框架。最初在2009年由加州大学伯克利分校的AMPLab开发,并于2010年成为Apache的开源项目之一。 在这个Apache Spark文章系列的第一部分中,我们将了解到什么是Spark,它与典型的MapReduce解决方案的比较以及它如何为大数据处理提供了一套完整的工具。希望本文档会给有需要的朋友带来帮助;感
- fork():异步提交子任务到当前线程的工作队列(不阻塞),类似“发号施令”,但不等结果
- join():阻塞等待对应 fork 出来的子任务完成,并获取其返回值(对 RecursiveTask)或完成状态(对 RecursiveAction)
- invoke():由外部调用,同步启动整个任务并等待最终结果,是入口方法(不能在 compute() 内调用)
典型模式是:判断是否足够小 → 直接计算;否则 fork 两个子任务 → 分别 join → 合并结果。
使用注意事项:别踩这些坑
- 任务粒度要合理:太粗,无法充分利用多核;太细,fork/join 开销反超收益。一般建议子任务执行时间在 100ns ~ 10ms 级别
- 避免阻塞操作:ForkJoinPool 不适合 IO 或锁等待任务,因为工作窃取依赖快速响应,阻塞会拖垮整个池
- 慎用共享变量:多个子任务可能并发修改同一对象,需自行加锁或用线程安全结构
- 默认使用公共池(ForkJoinPool.commonPool()),但可通过 new ForkJoinPool(parallelism) 自定义并行度(通常设为 CPU 核心数)
一个简单例子:并行计算数组和
用 RecursiveTask 实现:
class SumTask extends RecursiveTask{ final long[] arr; final int lo, hi; SumTask(long[] arr, int lo, int hi) { this.arr = arr; this.lo = lo; this.hi = hi; } protected Long compute() { if (hi - lo <= 1000) { // 阈值控制粒度 long sum = 0; for (int i = lo; i < hi; i++) sum += arr[i]; return sum; } int mid = (lo + hi) / 2; SumTask left = new SumTask(arr, lo, mid); SumTask right = new SumTask(arr, mid, hi); left.fork(); // 异步启动左半部分 long rightSum = right.compute(); // 当前线程直接算右半部分(避免再 fork) long leftSum = left.join(); // 等待左半部分结果 return leftSum + rightSum; } }
调用:new ForkJoinPool().invoke(new SumTask(arr, 0, arr.length))









