Fork/Join解读

简介: Fork/Join解读

概念

Fork/Join 是 JDK 1.7 加入的新的线程池实现,它体现的是一种分治思想,适用于能够进行任务拆分的 cpu 密集型 运算

所谓的任务拆分,是将一个大任务拆分为算法上相同的小任务,直至不能拆分可以直接求解。跟递归相关的一些计 算,如归并排序、斐波那契数列、都可以用分治思想进行求解

Fork/Join 在分治的基础上加入了多线程,可以把每个任务的分解和合并交给不同的线程来完成,进一步提升了运 算效率

Fork/Join 默认会创建与 cpu 核心数大小相同的线程池

关键点

Fork/Join(分而治之)是一种并行计算模型,用于解决递归性的、可以被分解为更小子任务的问题。它是 Java 7 引入的一个特性,并在 Java.util.concurrent 包中提供了相应的类来支持。

Fork/Join 模型基于以下两个关键操作:

  1. Fork(分解):将一个大任务分解为若干个小任务,并把这些小任务放入工作队列中,等待被执行。
  2. Join(合并):对小任务的执行结果进行合并,得到大任务的最终结果。

Fork/Join 模型的核心思想是递归地将问题划分为更小的子问题,直到子问题足够简单,可以直接求解。然后通过合并子问题的结果,最终得到原始问题的解。

在 Java 中,Fork/Join 模型的实现主要依赖于以下两个类:

  1. ForkJoinPool:是线程池的实现,用于管理任务的执行。它允许创建一个工作线程组,每个线程都有自己的工作队列。任务会被分发给空闲的工作线程执行,如果一个线程的工作队列为空,它可以从其他线程的队列中窃取任务来执行。
  2. RecursiveTask RecursiveAction:这两个抽象类是用来表示可分解的任务的。RecursiveTask 用于返回结果的任务,而 RecursiveAction 则用于不返回结果的任务。我们需要继承这些类,并实现 compute() 方法来执行任务划分和合并操作。

典型的使用场景包括在计算密集型任务中,例如大规模数据处理、图像处理、并行排序等。

Fork/Join 模型利用任务的递归分解和合并,能够充分利用多核处理器的性能,并提供了一种简洁高效的并行计算方式。

使用

提交给 Fork/Join 线程池的任务需要继承 RecursiveTask(有返回值)或 RecursiveAction(没有返回值),例如下 面定义了一个对 1~n 之间的整数求和的任务

1. class AddTask extends RecursiveTask<Integer> {
2. 
3. int n;
4. 
5. public AddTask(int n) {
6. this.n = n;
7.     }
8. 
9. @Override
10. 
11. public String toString() {
12. return "{" + n + '}';
13.     }
14. 
15. @Override
16. protected Integer compute() {
17. // 如果 n 已经为 1,可以求得结果了
18. 
19. if (n == 1) {
20.             System.out.println("join()"+n);
21. 
22. return n;
23.         }
24. 
25. // 将任务进行拆分(fork)
26. 
27. AddTask t1 = new AddTask(n - 1);
28.         t1.fork();
29.         System.out.println("fork()"+n+" "+ t1);
30. 
31. // 合并(join)结果
32. 
33. int result = n + t1.join();
34.         System.out.println("join()"+n+"+"+t1+"="+result );
35. return result;
36.     }
37. 
38. }

然后提交给 ForkJoinPool 来执行

1. public class Test {
2. public static void main(String[] args) {
3. ForkJoinPool pool = new ForkJoinPool(4);
4.         System.out.println(pool.invoke(new AddTask(5)));
5. 
6.     }
7. 
8. }

输出

fork()3 {2}

fork()5 {4}

fork()2 {1}

fork()4 {3}

join()1

join()2+{1}=3

join()3+{2}=6

join()4+{3}=10

join()5+{4}=15

15

用图来表示

改进

1. class AddTask3 extends RecursiveTask<Integer> {
2. 
3. int begin;
4. int end;
5. 
6. public AddTask3(int begin, int end) {
7. this.begin = begin;
8. this.end = end;
9.     }
10. 
11. @Override
12. 
13. public String toString() {
14. return "{" + begin + "," + end + '}';
15.     }
16. 
17. @Override
18. 
19. protected Integer compute() {
20. // 5, 5
21. if (begin == end) {
22.             log.debug("join() {}", begin);
23. return begin;
24.         }
25. // 4, 5
26. if (end - begin == 1) {
27.             log.debug("join() {} + {} = {}", begin, end, end + begin);
28. return end + begin;
29.         }
30. 
31. // 1 5
32. int mid = (end + begin) / 2;
33. 
34. // 3
35. AddTask3 t1 = new AddTask3(begin, mid);
36. 
37. // 1,3
38.         t1.fork();
39. AddTask3 t2 = new AddTask3(mid + 1, end);
40. // 4,5
41.         t2.fork();
42.         log.debug("fork() {} + {} = ?", t1, t2);
43. int result = t1.join() + t2.join();
44.         log.debug("join() {} + {} = {}", t1, t2, result);
45. return result;
46.     }
47. }

然后提交给 ForkJoinPool 来执行

1. public static void main(String[] args) {
2. ForkJoinPool pool = new ForkJoinPool(4);
3.  System.out.println(pool.invoke(new AddTask3(1, 10)));
4. }

结果

[ForkJoinPool-1-worker-0] - join() 1 + 2 = 3

[ForkJoinPool-1-worker-3] - join() 4 + 5 = 9

[ForkJoinPool-1-worker-0] - join() 3

[ForkJoinPool-1-worker-1] - fork() {1,3} + {4,5} = ?

[ForkJoinPool-1-worker-2] - fork() {1,2} + {3,3} = ?

[ForkJoinPool-1-worker-2] - join() {1,2} + {3,3} = 6

[ForkJoinPool-1-worker-1] - join() {1,3} + {4,5} = 15 15

用图来表示



相关文章
|
缓存 JavaScript 前端开发
JavaScript中DOM操作:新手常犯错误与避免策略
【4月更文挑战第1天】本文介绍了JavaScript中DOM操作的基础和新手常犯错误,包括频繁查询DOM、不恰当的遍历、滥用innerHTML、忽视异步与DOM状态以及过度同步更新。建议包括缓存DOM引用、注意文本节点、慎用innerHTML以防止XSS、正确处理异步和批量更新。遵循最佳实践,开发者能提升代码质量和应用性能。
496 2
|
弹性计算 Linux 开发工具
幻兽帕鲁服务器如何设置/修改密码
介绍了如何设置幻兽帕鲁服务器的密码,以及需要密码才可以加入到服务器中教程
14383 6
幻兽帕鲁服务器如何设置/修改密码
|
12月前
|
关系型数据库 MySQL
MySQL查看连接数和进程信息
这篇文章介绍了如何在MySQL中查看连接数和进程信息,包括当前打开的连接数量、历史成功建立连接的次数、连接错误次数、连接超时设置,以及如何查看和终止正在执行的连接进程。
1480 10
|
自然语言处理 IDE 测试技术
通义灵码怎么样?分为哪些版本,看看基础能力多少分?
通义灵码是一款基于通义大模型的智能编码辅助工具,提供实时代码续写、自然语言生成代码、单元测试生成、代码优化、注释生成、代码解释等功能。
|
安全 测试技术 数据库
基于SpringBoot+Vue作业管理系统(源码+部署说明+演示视频+源码介绍+lw)(3)
基于SpringBoot+Vue作业管理系统(源码+部署说明+演示视频+源码介绍+lw)
257 1
|
机器学习/深度学习 并行计算 PyTorch
PyTorch与CUDA:加速深度学习模型训练的最佳实践
【8月更文第27天】随着深度学习应用的广泛普及,高效利用GPU硬件成为提升模型训练速度的关键。PyTorch 是一个强大的深度学习框架,它支持动态计算图,易于使用且高度灵活。CUDA (Compute Unified Device Architecture) 则是 NVIDIA 开发的一种并行计算平台和编程模型,允许开发者直接访问 GPU 的并行计算能力。本文将详细介绍如何利用 PyTorch 与 CUDA 的集成来加速深度学习模型的训练过程,并提供具体的代码示例。
1391 3
|
传感器 数据可视化 数据管理
SolidWorks2021中文版【Solid Works 附补丁】完美破解版下载 安装教程
SolidWorks2021中文版【Solid Works 附补丁】完美破解版下载 安装教程
SolidWorks2021中文版【Solid Works 附补丁】完美破解版下载 安装教程
|
关系型数据库 MySQL
蓝易云 - MySQL自动删除binlog日志
注意,这个参数只影响新的binlog文件。如果你的服务器上已经有超过7天的日志文件,你需要手动删除它们,或者使用PURGE BINARY LOGS命令来删除它们。
129 0
|
Web App开发 缓存 小程序
提升微信小程序开发技能:高效实用的开发技巧与工具推荐
本文旨在帮助微信小程序开发工程师提升他们的开发技能,并介绍一些高效实用的开发技巧和工具,以提高开发效率和质量。我们将探讨一系列优化开发流程、提升代码质量、加速调试等方面的技巧,并推荐一些常用的工具,帮助开发工程师更好地进行微信小程序开发。
提升微信小程序开发技能:高效实用的开发技巧与工具推荐