应根据任务结果是否需被上层合并来选择:需合并用recursivetask,纯副作用用recursiveaction;compute()返回值类型直接决定任务类型与行为边界。

关键看任务结果是否要被上层合并使用——需要拼起来的选 RecursiveTask,只管做完不管返回的选 RecursiveAction。
任务输出是否是合并步骤的输入
这是最本质的判断依据:
- 如果子任务的结果必须传给 merge、sum、combine 等逻辑,用于生成最终答案,就必须用 RecursiveTask
。例如归并排序中左右子数组排好后要合并成一个有序数组,左结果和右结果就是 merge 的直接输入。 - 如果只是对数据做修改、写日志、打标记、发请求等操作,没有“结果值”可传递,那 RecursiveAction 更干净。比如把数组每个元素加 1,改完就完事,不需要返回新数组或累加值。
典型场景对比
RecursiveTask 适用场景(需汇总):
- 数组求和、最大值/最小值查找
- 归并排序、快速排序(返回已排序子段)
- 树遍历并统计节点数、计算表达式值
- 批量处理后需返回聚合结果,如过滤后返回匹配项列表
RecursiveAction 适用场景(纯副作用):
- 原地修改数组、集合元素(如全部乘以 2)
- 并行写文件、写数据库、发 HTTP 请求
- 图像分块处理(每块独立滤镜,不拼接)
- 状态标记:为对象树中符合条件的节点设 flag
误用会直接暴露问题
类型选错不是性能差异,而是逻辑断裂或编译失败:
- 该用 RecursiveTask 却写了 RecursiveAction:调用
left.join() + right.join()会编译报错——因为join()是 void,无法相加。 - 该用 RecursiveAction 却套了 RecursiveTask
:虽能编译,但徒增泛型擦除开销、返回值管理负担,还可能误导后续维护者以为有结果可取。 - 归并排序中用 RecursiveAction:merge 阶段拿不到左右子数组,要么空指针,要么逻辑崩溃,返回乱序结果。
代码签名一眼识别
不用翻文档,看 compute 方法声明就能确定:
-
protected Long compute()→ 必须是 RecursiveTask -
protected void compute()→ 只能是 RecursiveAction
这个方法签名决定了整个任务在 ForkJoin 框架中的行为边界:有没有值可 join,能不能参与结果组装,一目了然。










