forkjointask通过recursivetask实现并行二分搜索,核心是将查询数组按索引二分切分,每个子任务独立执行传统二分查找,再用fork()拆分、join()合并布尔结果数组,天然契合二分法的“分治+合并”结构。

直接用 ForkJoinTask 实现标准二分法的并行计算,核心是把“递归划分 + 合并结果”自然映射到 ForkJoinPool 的工作窃取模型上。关键不是强行多线程,而是让每个子任务足够独立、可合并,且划分开销远小于计算收益。
理解 ForkJoinTask 与二分法的匹配点
标准二分法(如二分查找、二分求根、归并排序中的分治段处理)本质是:问题规模减半 → 递归处理左右两半 → 合并结果。这和 ForkJoinTask 的 fork()(异步拆分)+ join()(同步等待并获取结果)天然契合。注意:它不适用于单次二分查找这种 O(log n) 的轻量操作,而适合对大规模数组做批量二分判定、并行二分搜索多个目标、或在大区间上高精度二分求解函数零点等场景。
写一个可运行的并行二分搜索任务(示例)
目标:在已排序长数组中,并行判断多个查询值是否存在。每个查询独立,但用二分逻辑;我们把“查询列表”按索引二分切分,每个子任务负责一段查询的二分判定。
步骤如下:
- 继承
RecursiveTask<boolean></boolean>(返回布尔数组表示各查询是否找到) - 定义字段:原数组
arr、查询值数组targets、当前处理的查询下标范围lo/hi - 在
compute()中:若hi - lo (比如 8),就用传统 for + 二分循环逐个查;否则切中点,<code>fork()左右两个子任务,再join()合并结果数组 - 合并时用
System.arraycopy拼接左右结果
代码片段(精简版):
class ParallelBinarySearch extends RecursiveTask<boolean> {
private final int[] arr;
private final int[] targets;
private final int lo, hi;
<pre class="brush:java;toolbar:false;">ParallelBinarySearch(int[] arr, int[] targets, int lo, int hi) {
this.arr = arr; this.targets = targets; this.lo = lo; this.hi = hi;
}
@Override
protected boolean[] compute() {
boolean[] res = new boolean[hi - lo];
if (hi - lo <= 4) {
for (int i = lo; i < hi; i++) {
res[i - lo] = binarySearchOne(arr, targets[i]);
}
return res;
}
int mid = lo + (hi - lo) / 2;
ParallelBinarySearch left = new ParallelBinarySearch(arr, targets, lo, mid);
ParallelBinarySearch right = new ParallelBinarySearch(arr, targets, mid, hi);
left.fork();
boolean[] rightRes = right.compute();
boolean[] leftRes = left.join();
System.arraycopy(leftRes, 0, res, 0, leftRes.length);
System.arraycopy(rightRes, 0, res, leftRes.length, rightRes.length);
return res;
}
private boolean binarySearchOne(int[] a, int key) {
int l = 0, r = a.length - 1;
while (l <= r) {
int m = l + (r - l) / 2;
if (a[m] == key) return true;
else if (a[m] < key) l = m + 1;
else r = m - 1;
}
return false;
}
}
启动并验证结果
用 ForkJoinPool.commonPool() 或自定义池提交任务:
int[] sorted = {1, 3, 5, 7, 9, 11, 13, 15};
int[] queries = {5, 8, 11, 2};
boolean[] found = new ForkJoinPool().invoke(new ParallelBinarySearch(sorted, queries, 0, queries.length));
// found = [true, false, true, false]
注意:不要在 compute() 中抛出受检异常;避免共享可变状态;阈值 THRESHOLD 需实测调整(太小导致任务过多,太大失去并行意义)。
常见坑与优化提示
别在 fork 前修改共享数据 —— 子任务可能并发执行,若你在切分前改了 arr,左右任务看到的可能是不同状态。
合并逻辑必须无副作用 —— join() 返回的是子任务的计算结果,不是引用原对象,所以拼接数组要新建或明确拷贝。
优先用 RecursiveTask 而非 ForkJoinTask 抽象类 —— 它自带泛型结果和 compute() 模板,更安全简洁。
监控并行度 —— 可通过 pool.getParallelism() 查看实际并行线程数,结合 CPU 核心数设置池大小。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











