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的更多相关文章

  1. Java Fork/Join 框架

    简介 从JDK1.7开始,Java提供Fork/Join框架用于并行执行任务,它的思想就是讲一个大任务分割成若干小任务,最终汇总每个小任务的结果得到这个大任务的结果. 这种思想和MapReduce很像 ...

  2. Fork/Join 框架-设计与实现(翻译自论文《A Java Fork/Join Framework》原作者 Doug Lea)

    作者简介 Dong Lea任职于纽约州立大学奥斯威戈分校(State University of New York at Oswego),他发布了第一个广泛使用的java collections框架实 ...

  3. java Fork/Join框架

    应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在很多的应用程序在运行在多核心 ...

  4. JAVA FORK JOIN EXAMPLE--转

    http://www.javacreed.com/java-fork-join-example/ Java 7 introduced a new type of ExecutorService (Ja ...

  5. java fork/join简单实践

    我们知道,java8中有并行流,而并行流在后台的实现是通过fork/join池来完成的,例如: List<Integer> a = buildList(); List<Integer ...

  6. Java fork join ForkJoinPool 用法例子

    本例是把一个大的数组求和的计算的大任务分解到在小范围内求和的小任务,然后把这些小任务之和加起来就是所求之结果. 技术:JDK8.0, Javafork-join模式下的RecursiveTask技术, ...

  7. Java并发——Fork/Join框架

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...

  8. 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验

    JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...

  9. 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing

    仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...

随机推荐

  1. 第15章: Prometheus监控Kubernetes资源与应用

    Prometheus监控Kubernetes资源与应用 目录 1 监控方案 2 2 监控指标 4 3 实现思路 4 4 在K8S中部署Prometheus 4 5 在K8S中部署Grafana与可视化 ...

  2. Burning Bridges 求tarjan求割边

    Burning Bridges 给出含有n个顶点和m条边的连通无向图,求出所有割边的序号. 1 #include <cstdio> 2 #include <cstring> 3 ...

  3. Mysql 主键的操作

    ​ 主键:primary key ,主要的键.一张表只能有一个字段可以使用对应的键,用来唯一的约束字段里面的数据,数据不能重复,这种键称之为主键,一张表只能最多有一个主键.  一.增加主键 方法一:在 ...

  4. Springboot:Springboot+Swagger2集成服务

    Swagger是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务.总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法,参数和模型紧密集成到服务 ...

  5. svn创建新分支报错:svn: E155015: Aborting commit: XXX remains in conflict

    用diea在对svn创建新分支的时候报错,错误为 svn: E155015: Aborting commit: XXX remains in conflict 百度和查阅资料后得知,此错误为分支被拉取 ...

  6. 6-x3 declare和typeset命令:设置变量属性

    declare 和 typeset 都是 Shell 内建命令,它们的用法相同,都用来设置变量的属性.不过 typeset 已经被弃用了,建议使用 declare 代替.declare 命令的用法如下 ...

  7. 大厂高级工程师面试必问系列:Java动态代理机制和实现原理详解

    代理模式 Java动态代理运用了设计模式中常用的代理模式 代理模式: 目的就是为其他对象提供一个代理用来控制对某个真实对象的访问 代理类的作用: 为委托类预处理消息 过滤消息并转发消息 进行消息被委托 ...

  8. vmware使用U盘安装系统

    创建好系统 创建一个新的硬盘,选择"physicalDrive1" 如果识别不到physicalDrive 1,使用下面的方法. 1.在本机的服务里面启用下面的服务. 2.重启 V ...

  9. C语言:带参数的宏与函数的区别

    带参数的宏和函数很相似,但有本质上的区别:宏展开仅仅是字符串的替换,不会对表达式进行计算:宏在编译之前就被处理掉了,它没有机会参与编译,也不会占用内存.而函数是一段可以重复使用的代码,会被编译,会给它 ...

  10. excel vslookup应用举例

    excel vslookup应用举例 =vslookup("第一个需要查找的对象","查找的区域范围","查找的最终目标在区域的第几列",& ...