Java Fork/Join
2021/5/8 22:26:45
本文主要是介绍Java Fork/Join,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Fork/Join框架
Fork/Join 以递归方式将可以并行的任务拆分成更小的任务,然后将每个子任务的结果合并起来生成整体结果。
这个过程其实就是分治算法的并行版本,图解如下:
如何使用
我们要使用 ForkJoin 框架,必须先创建一个 ForkJoinTask。它提供在任务中执行 fork() 和 join() 操作的机制,通常情况下我们不需要直接继承 ForkJoinTask 类,而只需要继承它的子类,Fork/Join 框架提供了以下两个子类:
- RecursiveAction:用于没有返回结果的任务。
- RecursiveTask :用于有返回结果的任务。
而ForkJoinTask 需要通过 ForkJoinPool 来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。
下面使用forkJoin来计算一个Integer List之和:
import com.google.common.collect.Lists; import com.sun.istack.internal.NotNull; import java.util.List; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.RecursiveTask; import java.util.stream.Collectors; import java.util.stream.IntStream; public class SumTask extends RecursiveTask<Long> { private static final int SPLIT_NUM = 10000; @NotNull private List<Integer> numberList; public SumTask(List<Integer> numberList) { this.numberList = numberList; } @Override protected Long compute() { // 不需要进行任务拆分 if (numberList.size() <= SPLIT_NUM) { return numberList.stream().mapToLong(Integer::intValue).sum(); } // 进行任务拆分 List<List<Integer>> splitNumberList = Lists.partition(numberList, numberList.size() / 2); List<SumTask> sumTasks = splitNumberList.stream().map(SumTask::new).collect(Collectors.toList()); // 执行子任务,继续拆分 invokeAll(sumTasks); // 合并结果 return sumTasks.stream().mapToLong(SumTask::join).sum(); } public static void main(String[] args) { ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); List<Integer> numberList = IntStream.rangeClosed(1, 100000).mapToObj(Integer::new).collect(Collectors.toList()); SumTask sumTask = new SumTask(numberList); Long sum = forkJoinPool.invoke(sumTask); System.out.println("1 ~ 100000 sum result is: " + sum); } }
上述代码的执行过程为,先将List 分为两个子List, 并发执行两个子List 的计算。然后再将子List 拆分为更小的List,依此往复,直至List无法再拆分时,计算其Sum,最后合并结果。
ForkJoinTask 与一般的任务的主要区别在于它需要实现 compute 方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用 fork 方法时,又会进入 compute 方法,形成递归调用,直到任务子任务不可再分。使用 join 方法会等待子任务执行完并得到其结果。
异常捕获
我们没办法在主线程中捕获ForkJoinTask 执行过程中抛出的异常。所以ForkJoinTask 提供了方法来检测Task 执行情况, 并提供了获取异常的方法。
// 检查Task 执行情况 sumTask.isCancelled(); sumTask.isCompletedNormally(); sumTask.isCompletedAbnormally(); // 获取异常信息 sumTask.getException();
实现原理
参考 https://segmentfault.com/a/1190000016781127
这篇关于Java Fork/Join的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-07-02springboot项目无法注册到nacos-icode9专业技术文章分享
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现
- 2024-06-03为什么以及如何要进行架构设计权衡?
- 2024-05-31全网首发第二弹!软考2024年5月《软件设计师》真题+解析+答案!(11-20题)