首页/新闻资讯/正文详情

11-Fork/Join 与工作窃取:并行流为什么能跑满 CPU,又有哪些坑

发布时间:2026/9/26 9:18:01 来源:云帆数科 栏目:资讯中心
11-Fork/Join 与工作窃取:并行流为什么能跑满 CPU,又有哪些坑
for循环处理 1000 万条数据单线程要跑 10 秒list.parallelStream()一行改成并发有时候 2 秒就完。魔法背后是Fork/Join 框架和工作窃取Work-Stealing。这一篇讲清它怎么把大任务拆小、怎么让空闲线程去偷别人的活以及并行流那些「用了反而更慢」的隐藏陷阱。一、Fork/Join 是什么Fork/Join 是 JDK 7 引入的并行框架专为「可以递归拆分成小块的 CPU 密集型任务」设计。核心思想Fork分解把大任务递归拆成足够小的子任务。Join合并等所有子任务算完把结果合并。它不是一个普通线程池而是ExecutorService的一个特殊实现ForkJoinPool用工作窃取算法把 CPU 吃满。二、工作窃取让核不闲着普通线程池的任务是「一个共享队列所有线程来抢」容易有竞争且一个线程干完就闲着。Fork/Join 反过来每个工作线程有自己的双端队列deque自己从头取任务干。当某线程做完了自己的队列线程 A 队列: [t1 t2 t3 t4 t5] ← A 从头部取干得快 线程 B 队列: [t6 t7 t8] ← B 慢 A 干完自己后从 B 队列的【尾部】偷一个任务来干「偷」发生在别人队列的另一端和主人取任务的端错开极大减少锁竞争。结果快的线程去帮慢的所有核尽量都在忙负载均衡自动达成。这正是它能「跑满 CPU」的原因。三、 RecursiveTask / RecursiveAction自己写 Fork/Join 任务继承这两个有返回值用RecursiveTask无返回值用RecursiveAction。关键是「拆到多小才停止」——阈值太小任务太多 overhead 大太大并行度不够。常见按数据量定阈值。classSumTaskextendsRecursiveTaskLong{staticfinalintTHRESHOLD1000;finallong[]arr;finalintlo,hi;SumTask(long[]a,intlo,inthi){arra;this.lolo;this.hihi;}protectedLongcompute(){if(hi-loTHRESHOLD){// 足够小直接算longs0;for(intilo;ihi;i)sarr[i];returns;}intmid(lohi)1;SumTaskleftnewSumTask(arr,lo,mid);SumTaskrightnewSumTask(arr,mid,hi);left.fork();// 异步拆出左半longrright.compute();// 当前线程直接算右半不浪费longlleft.join();// 等左半结果returnlr;}}longtotalnewForkJoinPool().invoke(newSumTask(big,0,big.length));注意left.fork()后当前线程right.compute()而不是right.fork()——避免多开一个线程空等这是标准写法能少一次线程占用。四、并行流Fork/Join 的语法糖Stream.parallel()/parallelStream()就是把流水线交给公共ForkJoinPool.commonPool()跑longsumlist.parallelStream().filter(x-x0).mapToLong(x-x).sum();底层自动用 Fork/Join 把数据切片、各核并行处理、再合并。写起来一行背后是完整的工作窃取并行。五、并行流的 5 个坑重点共享可变状态 → 数据错乱并行流里千万别改外部共享变量累加要用reduce/collect返回新值别用外部total x// 错误多线程改同一个 total结果错longtotal0;list.parallelStream().forEach(x-totalx);// 正确用 reduce每个线程算自己的局部和再合并longtotallist.parallelStream().reduce(0L,Long::sum);用公共池跑 IO 阻塞commonPool默认并行度 核数-1且是守护线程、全局共享。若在并行流里做 DB/HTTP 调用阻塞会占满公共池拖垮所有用并行流的地方包括其他业务的CompletableFuture。数据量小反而更慢拆任务、合并、线程协调都有开销。几千条以下的数据并行流的 overhead 可能超过并行收益直接用串行流更快。ThreadLocal失效公共池线程是复用的、不绑定你的调用线程链ThreadLocal/InheritableThreadLocal在并行流里不按预期传递要用TL传上下文会丢。顺序敏感操作不能用forEach在并行下不保证顺序需要顺序用forEachOrdered但会牺牲并行度。findFirst在并行下也可能比串行慢要保持顺序语义。六、什么时候该用什么时候别用场景建议大数据量 CPU 密集求和/过滤/转换✅ 并行流首选数据量小 数千❌ 直接用串行任务里有 IO/网络/锁❌ 别用并行流用自定义 IO 线程池需要强顺序输出⚠️ 用forEachOrdered收益打折要控制并行度/隔离⚠️ 自己new ForkJoinPool()提交别用公共池自定义池隔离示例避免污染公共池ForkJoinPoolpoolnewForkJoinPool(8);longrpool.submit(()-list.parallelStream().reduce(0L,Long::sum)).get();七、和线程池的关系Fork/Join 是为「计算」而生的并行框架不适合长阻塞任务。日常业务「异步处理 IO」还是用普通ThreadPoolExecutor只有「可拆分的大计算」才上 Fork/Join / 并行流。两者不是替代关系是分工IO/任务调度用线程池纯计算拆并用 Fork/Join。总结Fork/Join 用「分治 工作窃取」把 CPU 吃满每个线程有自己双端队列干完去偷别人队列尾部的任务自动负载均衡。并行流是它的一行语法糖。reduce/collect返回的合并才线程安全别在并行流里改共享变量公共commonPool是全局共享的守护线程池跑 IO 阻塞会拖垮所有并行流用户小数据量、强顺序、依赖ThreadLocal时别用并行流。IO 密集型仍用普通线程池Fork/Join 专攻可拆分的大计算。

相关推荐

AI代码审查副驾驶:OpenCodeReview如何用大模型提升Code Review效率
AI代码审查副驾驶:OpenCodeReview如何用大模型提升Code Review效率

1. 项目背景:为什么代码审查需要一颗“AI副驾驶”1.1 那些年我们被 Code Review 折磨的时刻先说一个让我下定决心做 open-code-review 的场景。那是在一家成长很快的创业公司,团队从5个人扩张到30多个人,PR 数量从每天几个涨到几十个。代码审… · 2026/9/26 9:18:01

AI辅助代码评审实战:从部署到定制,打造高效Code Review流程
AI辅助代码评审实战:从部署到定制,打造高效Code Review流程

聊代码评审这个事儿,几乎每个团队都头疼过。PR挂着没人看、好不容易有人看了也是敷衍两句、线上出 bug 之后互相甩锅“这行当时不是你写的吗”。我过去在团队里推行 Code Review 也失败过几轮,后来自己折腾开源方案,才慢慢找到一套顺手的打法… · 2026/9/26 9:17:55

Spring MVC 获取 Cookie、Session 与 Header 详解
Spring MVC 获取 Cookie、Session 与 Header 详解

一、前言:为什么需要 Cookie 与 Session?HTTP 协议本身是 无状态 的,即客户端与服务器之间的每次通信都是独立的,服务器无法默认识别两次请求是否来自同一用户。但是在实际开发中,我们往往需要知道请求之间的关联关系&… · 2026/9/26 9:17:55

Cursor平替Void开源AI编程工具实测:TaoToken统一Key接入与config.toml配置验证
Cursor平替Void开源AI编程工具实测:TaoToken统一Key接入与config.toml配置验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:40:02

ETest 装备软件测试平台配 TaoToken:国产 CPU 与 OS 下的统一 Key 接入配置骨架
ETest 装备软件测试平台配 TaoToken:国产 CPU 与 OS 下的统一 Key 接入配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:40:02

CurSor AI编程系列(一)入门及部署:TaoToken 统一 Key 配置与 settings.json 骨架
CurSor AI编程系列(一)入门及部署:TaoToken 统一 Key 配置与 settings.json 骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:40:02

SQL中的函数:从聚合函数到自定义函数的T-SQL实战配置
SQL中的函数:从聚合函数到自定义函数的T-SQL实战配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:40:02

Hadoop实现商品推荐系统:Item-CF离线计算实战
Hadoop实现商品推荐系统:Item-CF离线计算实战

简介:本资源是一套基于Hadoop生态构建的商品推荐系统实践项目,面向大数据初学者与分布式计算入门开发者,聚焦电商场景下的用户行为分析与个性化推荐落地。项目依托HDFS分布式存储与MapReduce批处理框架,完成从用户-商品交互数据采… · 2026/9/26 10:40:02

用 Claude Code 做代码质量审查与风险评估:TaoToken 统一 Key 接入与 settings.json 配置实战
用 Claude Code 做代码质量审查与风险评估:TaoToken 统一 Key 接入与 settings.json 配置实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 10:39:50

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码