compute()方法是清晰可追踪的物理执行链路:任务在栈帧中逐层拆分并提交至工作线程或本地队列,完成后逐层回传结果;每次进入compute()先依自定义终止条件(如end-start)判断是否足够小。

compute() 方法不是黑箱,它是一条清晰可追踪的物理执行链路:从初始调用开始,任务在栈帧中逐层拆分,子任务被提交到工作线程或本地队列,完成后再逐层回传结果——整个过程完全对应 CPU 线程的实际调度与栈展开/收缩行为。
拆分动作发生在 compute() 内部判断之后
每次进入 compute(),第一件事是检查当前任务是否“足够小”。这个判断靠的是你写的终止条件,比如 end - start 。不满足就拆,满足就直接算。
- 拆分不是自动发生的,是你在 compute() 里主动 new 子任务、调 fork() 或 compute() 实现的
- 常见写法是取中点,构造左右两个新任务,比如 new SumTask(arr, start, mid) 和 new SumTask(arr, mid, end)
- 注意:只拆一次不递归,递归靠的是子任务自己的 compute() 被再次调用——这是栈层层下压的过程
合并动作由 join() 或 compute() 的返回值自然触发
拆完不等于完事,必须等子任务结果回来才能合并。这里没有隐式等待,所有等待都是显式方法调用带来的线程阻塞或同步点。
- left.fork() 把左任务扔进队列,当前线程立刻继续往下走
- right.compute() 是同步执行右任务,当前线程亲自算,不新建栈帧也不交出去
- left.join() 阻塞当前线程,直到左任务完成并返回结果;这是合并前最后的同步点
- 两结果拿到后,return leftResult + rightResult 就是物理意义上的“合并”——一次普通加法指令
整个链路在栈和队列中真实可见
你可以把每次 compute() 调用想象成一个函数栈帧;每次 fork() 相当于往某个线程的工作队列里塞一个待办事项;每次 join() 就是当前帧暂停,等另一个帧结束再取回值。
- 主线程调 compute() → 压入第 1 层栈帧 → 拆 → fork 左 → compute 右 → 右完成 → join 左 → 左完成 → 合并 → 返回
- 左任务被 fork 后,可能由同一线程“偷”来执行(工作窃取),也可能由其他空闲线程取出执行,但它的 compute() 会压入新的栈帧,形成第 2 层
- 每层栈帧只管自己那块数据的拆或合,不跨层、不越界,边界由 start/end 或数组索引严格定义
阈值不是魔法数字,而是平衡点
THRESHOLD 决定了递归在哪一层收口。设得太小,fork/join 开销压倒计算收益;设得太大,又没法充分利用多核。
- 典型经验值在 100–1000 之间,具体看任务粒度(如数组求和常用 500,矩阵乘可能用 32)
- 它不改变逻辑正确性,只影响性能——就像齿轮比,调不对就费力不讨好
- 调试时可临时打日志:System.out.println("compute [" + start + "," + end + ")"),一眼看清拆分深度和范围










