RecursiveAction 是 ForkJoinTask 的无返回值子类,专用于并行递归计算;必须提交至 ForkJoinPool 以利用工作窃取机制,不可用 new Thread() 或普通 ExecutorService。

RecursiveAction 是 ForkJoinTask 的子类,专为「无返回值」的并行递归计算设计。如果你的任务不需要返回结果(比如批量更新对象、写文件、触发副作用),用它比 RecursiveTask 更轻量、更安全。
为什么不能直接 new Thread() 或用 ExecutorService?
手动管理线程无法利用工作窃取(work-stealing)机制,容易造成负载不均;而 ExecutorService 的普通线程池不支持任务拆分与自动合并。只有 ForkJoinPool 能高效调度嵌套的 RecursiveAction 子任务。
- 必须把任务提交到
ForkJoinPool实例,不能用new Thread().start() - 不要在
compute()里调用join()等待自己 —— 这会死锁 -
invoke()是阻塞式提交,适合主流程等待;fork()+join()用于组合多个子任务
compute() 方法里怎么拆分任务?
拆分逻辑完全由你定义,但关键原则是:有明确的「停止递归条件」+ 「子任务能独立执行」。常见模式是按数据范围切片,比如处理数组、列表或树节点。
- 典型终止条件:
end - start (小到不用再分) - 拆分后必须调用
fork()启动子任务,而不是直接compute() - 两个子任务都
fork()后,当前任务可继续做其他事,最后用join()确保完成
示例:对数组每个元素加 1
class IncrementAction extends RecursiveAction {
final int[] arr;
final int start, end;
static final int THRESHOLD = 1000;
<pre class="brush:php;toolbar:false;">IncrementAction(int[] arr, int start, int end) {
this.arr = arr;
this.start = start;
this.end = end;
}
protected void compute() {
if (end - start <p>}</p>常见错误:忘记 fork()、误用 join()、共享状态没同步
最常踩的坑不是语法,而是并发语义理解偏差:
-
fork()不等于立即执行 —— 它只是把任务放入队列,由 ForkJoinWorkerThread 拿去跑 - 如果子任务修改了共享变量(如
ArrayList),且没加锁或用线程安全容器,结果不可预测 - 在
compute()外部调用join()会抛IllegalThreadStateException—— 只能在 fork() 后的同一线程内 join() - 过度拆分(THRESHOLD 设太小)会导致任务调度开销压倒计算收益
真正难的不是写对 compute(),而是判断「这个操作是否真的适合递归并行化」—— 如果每次子任务耗时远小于上下文切换成本,或者存在强顺序依赖,RecursiveAction 反而拖慢速度。










