ForkJoinPool 详解

发布时间:2026/10/15 19:36:23
ForkJoinPool 详解 ForkJoinPool是 JDK7 引入的分支 / 合并Fork/Join线程池专门用于可以递归拆分的任务核心思想分而治之。适用场景任务可以拆成多个子任务子任务合并结果典型递归遍历、并行排序、大数据集合计算。 不适合IO 阻塞型任务数据库、网络调用会严重浪费线程。一、核心思想Fork Joinfork拆分把大任务切割成若干小的子任务交给线程池并行执行join合并等待所有子任务执行完成收集子任务结果合并成最终结果。类比统计 1000 万条数据求和 → 拆成 10 个 100 万的子任务并行计算最后把 10 个结果相加。二、核心类表格类作用ForkJoinPool线程池主体管理工作线程、任务队列ForkJoinTaskV任务抽象类相当于Runnable/Callable支持 fork/joinRecursiveTaskV有返回值的任务最常用重写compute()RecursiveAction无返回值任务重写compute()CountedCompleterTJDK8 新增完成回调链式任务适合异步流水线RecursiveTask 模板代码求和示例class SumTask extends RecursiveTaskLong { private long[] arr; private int start; private int end; // 阈值小于该值就不再拆分直接串行计算 private static final int THRESHOLD 1000; public SumTask(long[] arr, int start, int end) { this.arr arr; this.start start; this.end end; } Override protected Long compute() { // 任务足够小直接计算 if (end - start THRESHOLD) { long sum 0; for (int i start; i end; i) { sum arr[i]; } return sum; } // 拆分任务 int mid (start end) / 2; SumTask left new SumTask(arr, start, mid); SumTask right new SumTask(arr, mid, end); // fork 提交子任务 left.fork(); right.fork(); // join等待子任务结果并合并 return left.join() right.join(); } } // 使用 public static void main(String[] args) { long[] array new long[20000]; Arrays.fill(array, 1); ForkJoinPool pool new ForkJoinPool(); Long result pool.invoke(new SumTask(array, 0, array.length)); System.out.println(result); pool.shutdown(); }三、核心机制工作窃取 Work-Stealing这是 ForkJoinPool 和普通ThreadPoolExecutor最大区别每个工作线程WorkerThread都有一个双端队列 Deque线程优先处理自己队列里的任务当自己队列空了去其他线程的队列尾部偷任务减少竞争从尾部拿对方从头部取目的最大化 CPU 利用率避免线程空闲。普通线程池所有线程共用一个任务队列多线程抢同一个队列锁竞争高。⚠️ 注意队列存放的是ForkJoinTask不是普通 Runnable。四、ForkJoinPool 构造器与参数public ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory, UncaughtExceptionHandler handler, boolean asyncMode)parallelism并行度默认Runtime.getRuntime().availableProcessors()不等于最大线程数代表期望并行数量factory创建 worker 线程工厂handler任务异常处理器asyncMode是否异步模式。false默认FIFO队列 LIFO适合递归 fork/join 任务trueFIFO适合不 join 的异步事件任务。JDK8 新增ForkJoinPool.commonPool()公共池ForkJoinPool commonPool ForkJoinPool.commonPool();全局共享不要 shutdownStream 并行流parallelStream()默认使用这个公共池并行度默认 CPU 核心数 -1风险如果在 commonPool 执行阻塞任务会占满公共池影响所有使用并行流的代码。五、提交任务 4 种方式invoke(task)阻塞等待任务完成返回结果submit(task)非阻塞返回ForkJoinTask后续用get()/join()拿结果execute(task)无返回异步执行commonPool()静态提交。join() vs get()join()ForkJoinTask 专属抛出运行时异常 CompletionException不需要显式 catch 受检异常get()继承 Future抛出受检异常InterruptedException, ExecutionException推荐在 ForkJoin 任务内部用join()。六、任务拆分的阈值THRESHOLD怎么选阈值不是越小越快阈值太小 → 任务拆分过多创建大量 ForkJoinTask 对象对象开销 并行收益阈值太大 → 拆分太少并行度不足CPU 跑不满经验根据任务计算耗时测试调整CPU 密集型任务单任务执行 1ms~10ms 比较合适。七、重要特性 坑点✅ 优点工作窃取CPU 利用率高天然支持递归分治任务JDK8 parallelStream 底层基于 common ForkJoinPool。❌ 缺点与坑只适合 CPU 密集型不能做 IO 阻塞任务数据库、http线程阻塞后工作窃取机制不会自动扩容线程线程被卡住并行度下降。commonPool全局共享一个任务阻塞影响整个应用并行流任务拆分有开销简单小任务不要用 ForkJoin串行更快递归深度过大会创建大量任务内存上涨异常处理子任务异常会在 join/get 时抛出异常会向上传播不要在 ForkJoin 任务内部直接新建线程。八、ForkJoinPool vs ThreadPoolExecutor表格特性ForkJoinPoolThreadPoolExecutor任务模型分治递归支持 fork/join独立任务队列每个线程私有双端队列 工作窃取全局共享阻塞队列适用场景CPU 密集、可拆分任务并行流通用场景IO/CPU 都可线程管理并行度动态窃取任务核心线程、最大线程队列排队九、JDK8 Stream parallelStream 注意list.parallelStream().forEach(xxx);底层就是ForkJoinPool.commonPool()坑并行流里不要操作非线程安全变量ArrayList 等会并发修改异常 如果需要自定义线程池执行 parallelStream不能直接给 stream 设置池需要把并行流代码包装在自定义 ForkJoinPool 任务里执行。十、简单性能结论任务是 CPU 密集、可以拆分 → ForkJoinPool 优于普通线程池IO 任务、短任务、不可拆分任务 → 普通 ThreadPoolExecutor 更合适。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询