news 2026/10/15 19:35:56

ForkJoinPool 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ForkJoinPool 详解

ForkJoinPool是 JDK7 引入的分支 / 合并(Fork/Join)线程池,专门用于可以递归拆分的任务,核心思想:分而治之。

适用场景:任务可以拆成多个子任务,子任务合并结果;典型:递归遍历、并行排序、大数据集合计算。 不适合:IO 阻塞型任务(数据库、网络调用),会严重浪费线程。

一、核心思想:Fork & Join

  1. fork(拆分):把大任务,切割成若干小的子任务,交给线程池并行执行;
  2. join(合并):等待所有子任务执行完成,收集子任务结果,合并成最终结果。

类比:统计 1000 万条数据求和 → 拆成 10 个 100 万的子任务并行计算,最后把 10 个结果相加。

二、核心类

表格

类作用
ForkJoinPool线程池主体,管理工作线程、任务队列
ForkJoinTask<V>任务抽象类,相当于Runnable/Callable,支持 fork/join
RecursiveTask<V>有返回值的任务(最常用),重写compute()
RecursiveAction无返回值任务,重写compute()
CountedCompleter<T>JDK8 新增,完成回调链式任务,适合异步流水线

RecursiveTask 模板代码(求和示例)

class SumTask extends RecursiveTask<Long> { 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最大区别

  1. 每个工作线程(WorkerThread)都有一个双端队列 Deque;
  2. 线程优先处理自己队列里的任务;
  3. 当自己队列空了,去其他线程的队列尾部偷任务(减少竞争,从尾部拿,对方从头部取);
  4. 目的:最大化 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 任务;
    • true:FIFO,适合不 join 的异步事件任务。

JDK8 新增ForkJoinPool.commonPool()公共池

ForkJoinPool commonPool = ForkJoinPool.commonPool();
  • 全局共享,不要 shutdown;Stream 并行流parallelStream()默认使用这个公共池;
  • 并行度默认 = CPU 核心数 -1;
  • 风险:如果在 commonPool 执行阻塞任务,会占满公共池,影响所有使用并行流的代码。

五、提交任务 4 种方式

  1. invoke(task):阻塞,等待任务完成,返回结果;
  2. submit(task):非阻塞,返回ForkJoinTask,后续用get()/join()拿结果;
  3. execute(task):无返回,异步执行;
  4. commonPool()静态提交。

join() vs get()

  • join():ForkJoinTask 专属,抛出运行时异常 CompletionException,不需要显式 catch 受检异常;
  • get():继承 Future,抛出受检异常InterruptedException, ExecutionException;

推荐在 ForkJoin 任务内部用join()。

六、任务拆分的阈值(THRESHOLD)怎么选?

阈值不是越小越快:

  • 阈值太小 → 任务拆分过多,创建大量 ForkJoinTask 对象,对象开销 > 并行收益;
  • 阈值太大 → 拆分太少,并行度不足,CPU 跑不满;

经验:根据任务计算耗时测试调整,CPU 密集型任务,单任务执行 1ms~10ms 比较合适。

七、重要特性 & 坑点

✅ 优点

  1. 工作窃取,CPU 利用率高;
  2. 天然支持递归分治任务;
  3. JDK8 parallelStream 底层基于 common ForkJoinPool。

❌ 缺点与坑

  1. 只适合 CPU 密集型,不能做 IO 阻塞任务(数据库、http)

    线程阻塞后,工作窃取机制不会自动扩容线程,线程被卡住,并行度下降。

  2. commonPool全局共享,一个任务阻塞,影响整个应用并行流;
  3. 任务拆分有开销,简单小任务不要用 ForkJoin,串行更快;
  4. 递归深度过大,会创建大量任务,内存上涨;
  5. 异常处理:子任务异常会在 join/get 时抛出,异常会向上传播;
  6. 不要在 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 更合适。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 19:35:08

论文降aigc实测:知网AI率95%降到3.7%,2026年10月AIGC检测最新结果!

反复打磨论文内容&#xff0c;查重顺利过关&#xff0c;格式来回调整好几轮&#xff0c;导师初审也点头认可。可提交AIGC检测的那一刻报告一打开&#xff0c;AI率直接飘红定稿之路瞬间被卡住。 不少同学第一反应就是手动急救逐句调换语序、替换近义词&#xff0c;熬上一两个通…

作者头像 李华
网站建设 2026/10/15 19:34:30

关于博士培养体系建设与学术能力提升的相关研究梳理

外文文献“白嫖”与高效阅读全攻略 宝子们&#xff0c;是不是一提到下载外文文献就头大&#xff1f; 想找的文献要么全网搜不到&#xff0c;要么下载流程复杂到让人原地爆炸。好不容易在某个小网站找到了资源&#xff0c;又担心版权问题不敢点&#xff0c;更别说有些平台动辄几…

作者头像 李华
网站建设 2026/10/15 19:34:12

AI文献检索实用指南 高效获取专业学术文献的检索方法与技巧解析

外文文献“白嫖”与高效阅读全攻略 宝子们&#xff0c;是不是一提到下载外文文献就头大&#xff1f; 想找的文献要么全网搜不到&#xff0c;要么下载流程复杂到让人原地爆炸。好不容易在某个小网站找到了资源&#xff0c;又担心版权问题不敢点&#xff0c;更别说有些平台动辄几…

作者头像 李华
网站建设 2026/10/15 19:32:47

基于Asio2实现http服务器的实践总结(二)

目录 1.基于asio2实现的http示例代码 2.修改服务线程数 3.在绑定的路由请求中获取请求的远程的IP和端口 4.静态资源绑定 5.在 url 中传递参数的原理 5.1.简介 5.2.浏览器本地预处理&#xff08;输入 URL 后&#xff0c;发请求前&#xff09; 5.3.建立 TCP 连接 5.4.浏…

作者头像 李华
网站建设 2026/10/15 19:32:01

bootstrap-fileinput生产级文件上传插件实战指南

简介&#xff1a;本资源为Bootstrap FileInput文件上传插件的完整前端集成包&#xff0c;面向Web前端开发者及Bootstrap项目实践者&#xff0c;解决传统文件上传交互简陋、预览缺失、样式割裂等常见痛点。压缩包共3个文件&#xff08;2个JS脚本 1个CSS样式表&#xff09;&…

作者头像 李华