当前位置: 首页 > news >正文

Fork/Join框架:CountedCompleter与RecursiveTask深度对比

Java Fork/Join框架:三大核心组件深度解析-CSDN博客 讨论过CountedCompleter 与 RecursiveTask 的差异,这里做一个总结。

CountedCompleter 在排序中应用见:深入浅出 Arrays.sort(DualPivotQuicksort):如何结合快排、归并、堆排序和插入排序-CSDN博客

CountedCompleter 相比传统的 RecursiveTask 在特定场景下具有显著优势,尤其在​​任务完成后需要自动触发后续操作​​或​​任务结构动态变化​​时。以下是详细分析及代码示例的解析:


​核心优势对比​

​特性​CountedCompleterRecursiveTask
​结果依赖​不强制返回结果,专注完成动作需返回子任务结果并显式合并
​完成触发机制​子任务完成后自动调用 onCompletion()需手动 join() 子任务并处理结果
​任务依赖管理​通过挂起计数(pending count)自动管理需手动调用 fork()/join() 管理
​资源开销​避免阻塞线程,减少上下文切换join() 可能阻塞线程增加开销
​动态子任务​支持运行时动态增加子任务子任务数量需预先确定

​代码示例解析:并行排序的优化点​

1. ​​任务链自动触发合并(onCompletion)​

// Sorter.onCompletion()
public void onCompletion(CountedCompleter<?> caller) {if (depth < 0) {// 自动触发合并操作(Merger任务)new Merger(...).invoke();}
}

  • ​优势​​:排序子任务完成后,无需手动聚合结果,onCompletion() 自动触发合并逻辑。
  • ​对比 RecursiveTask​:需在 compute() 中手动 join() 子任务并调用合并方法。

2. ​​避免线程阻塞​

// Sorter.compute()
new Sorter(...).fork();  // 提交子任务但不阻塞
new Sorter(...).compute(); // 当前线程执行另一个子任务
tryComplete(); // 非阻塞式更新状态

  • ​优势​​:通过 tryComplete() 递减挂起计数,子任务完成时自动传播完成状态,无阻塞。
  • ​对比 RecursiveTask​:必须调用 join() 等待子任务完成,阻塞当前线程。

3. ​​动态子任务管理​

// forkSorter() 动态添加任务
private void forkSorter(int depth, int low, int high) {addToPendingCount(1); // 动态增加挂起计数new Sorter(...).fork(); // 添加新任务
}

  • ​优势​​:运行时灵活增减子任务(如负载均衡),addToPendingCount(1) 调整计数。
  • ​对比 RecursiveTask​:子任务数量在初始化时必须固定,缺乏灵活性。


​适用场景​

  • ​推荐 CountedCompleter
    任务完成后需自动触发后续操作(如排序的合并阶段)、任务结构动态变化、避免阻塞线程时。

    • ​典型场景​​:并行排序、图遍历、流水线任务链。
  • ​推荐 RecursiveTask
    分治任务结果明确且简单(如数组求和、斐波那契数),无需复杂完成回调。


​总结​

在 Arrays.sort 中,SorterMerger 使用 CountedCompleter 实现了:

  1. ​任务链自动化​​:排序后自动触发合并。
  2. ​零阻塞​​:通过挂起计数和 tryComplete() 避免 join() 阻塞。
  3. ​动态扩展​​:运行时灵活增减子任务。
  4. ​资源高效​​:复用缓冲区,减少内存分配。

RunMerger(基于 RecursiveTask)需手动合并结果且可能导致阻塞。因此,在复杂任务依赖或非阻塞设计场景下,​CountedCompleter 在性能、灵活性和代码简洁性上更具优势​​。

tryComplete

tryComplete() 方法的实现通过一个 ​​循环 + CAS 机制​​ 实现了非阻塞的任务完成通知,避免线程阻塞。下面详细解释其工作原理:

public final void tryComplete() {CountedCompleter<?> a = this, s = a;  // a: 当前任务, s: 原始调用任务for (int c;;) {// 情况1:当前任务挂起计数为0if ((c = a.pending) == 0) {a.onCompletion(s);           // 1️⃣ 触发完成回调if ((a = (s = a).completer) == null) {s.quietlyComplete();     // 2️⃣ 根任务直接完成return;}}// 情况2:CAS减少挂起计数else if (a.weakCompareAndSetPendingCount(c, c - 1)) return;                     // 3️⃣ 递减成功直接退出}
}

循环中的三种状态转移​

​状态1:当前任务挂起计数为0​​ ✅

  1. 调用 onCompletion(s) 执行后置处理(如合并排序结果)

  2. 向上回溯父任务 (a = a.completer)

  3. ​循环继续处理父任务​​(若父任务存在)

​状态2:成功减少挂起计数​​ ▼

  1. 通过 CAS (weakCompareAndSetPendingCount) 将挂起计数从 cc-1
  2. ​直接退出循环​​(当前任务仍有未完成子任务)

​状态3:CAS 竞争失败​​ 🔁

  1. CAS 失败意味着其他线程修改了挂起计数
  2. ​自动进入下一轮循环​​,重新检测状态

相关文章:

  • C语言 — 通讯录模拟实现
  • 135. 分发糖果
  • 2.1.1 通信基础的基本概念
  • F(x,y)= 0 隐函数 微分法
  • sizeof 与strlen的区别
  • 基于规则的自然语言处理
  • 进程与线程的区别
  • 5-C#的DateTime使用
  • 2025.6.8
  • java 时区时间转为UTC
  • 一种停车场自动停车导航器的设计(论文+源码)
  • 31.2linux中Regmap的API驱动icm20608实验(编程)_csdn
  • 【存储基础】对象存储基础知识
  • 动态生成 PV 的机制:使用 NFS-Client Provisione
  • Python训练打卡Day43
  • Angular中Webpack与ngx-build-plus 浅学
  • nodejs环境变量配置
  • Day25 异常处理
  • 力扣HOT100之二分查找:4. 寻找两个正序数组的中位数
  • pymongo配置事务环境并封装事务功能
  • 企业免费网站系统/关键词排名推广软件
  • 北京网站建设石榴汇/seo关键词怎么填
  • 企业做电商网站有哪些内容/吸引人气的营销方案
  • 关于开通网站建设的请示/宣传推广计划
  • 烟台城乡建设住建局网站/关键词搜索趋势
  • 好的 做网站的软件公司/广州优化公司哪家好