Fork/Join 适合能递归拆分、子任务相互独立且计算成本足够大的 CPU 密集工作。任务太小会让调度成本超过收益,阻塞 I/O 则会占住有限工作线程。本文用求和任务观察拆分阈值和工作窃取。
Table of contents
Open Table of contents
运行递归求和
下载ForkJoin 求和样例下载, 运行:
java -cp target/classes dev.elaine.concurrency.ForkJoinSumSample
输出为 求和=500000500000。任务区间小于阈值时顺序计算,否则拆成左右两部分。示例先 fork 左任务,在当前线程计算右
任务,最后 join 左结果,避免当前线程只负责等待。
工作窃取减少空闲
ForkJoinPool 为工作线程维护双端队列。线程优先处理自己队列中的任务;空闲时从其他线程队列窃取任务。**工作窃取 (Work Stealing)**能在子任务耗时不均时提高核心利用率,但不能消除共享内存、缓存和调度成本。
阈值过小会产生大量任务对象和队列操作;过大则并行度不足。使用 JMH 或完整负载比较不同阈值,不从 CPU 核数直接推导 一个固定数字。
commonPool 是共享资源
并行流和未指定 Executor 的许多 CompletableFuture.*Async 方法使用 ForkJoinPool.commonPool()。一个模块提交长阻塞
任务,可能拖慢另一个看似无关的并行流。
long total = values.parallelStream().mapToLong(Long::longValue).sum();
这行代码只有在数据量足够、转换 CPU 密集、无副作用且测量有收益时才值得并行。数据库查询、远程请求、共享锁和小列表 通常不合适。
ManagedBlocker 不是默认答案
ForkJoinPool 提供 ManagedBlocker,让池在已知阻塞期间补偿工作线程。但封装任意 I/O 会增加复杂度,且下游容量仍然
有限。阻塞式高并发任务在 Java 21+ 通常优先评估虚拟线程;Fork/Join 继续服务 CPU 分治计算。
并行测量清单
- 先建立顺序实现和正确性测试。
- 固定输入规模、分布和机器核心数。
- 预热 JVM,并使用多次独立进程。
- 比较墙钟时间、CPU 时间、分配量和吞吐。
- 检查生产中是否与其他 commonPool 使用者竞争。
Java 25 的 ForkJoinPool API 描述了工作窃取、阻塞补偿和池状态指标。
下一步
阅读ThreadLocal 泄漏与请求上下文治理,观察线程复用如何把上一个请求的 数据带入下一个请求。