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的更多相关文章
- Java Fork/Join 框架
简介 从JDK1.7开始,Java提供Fork/Join框架用于并行执行任务,它的思想就是讲一个大任务分割成若干小任务,最终汇总每个小任务的结果得到这个大任务的结果. 这种思想和MapReduce很像 ...
- Fork/Join 框架-设计与实现(翻译自论文《A Java Fork/Join Framework》原作者 Doug Lea)
作者简介 Dong Lea任职于纽约州立大学奥斯威戈分校(State University of New York at Oswego),他发布了第一个广泛使用的java collections框架实 ...
- java Fork/Join框架
应用程序并行计算遇到的问题 当硬件处理能力不能按摩尔定律垂直发展的时候,选择了水平发展.多核处理器已广泛应用,未来处理器的核心数将进一步发布,甚至达到上百上千的数量.而现在很多的应用程序在运行在多核心 ...
- JAVA FORK JOIN EXAMPLE--转
http://www.javacreed.com/java-fork-join-example/ Java 7 introduced a new type of ExecutorService (Ja ...
- java fork/join简单实践
我们知道,java8中有并行流,而并行流在后台的实现是通过fork/join池来完成的,例如: List<Integer> a = buildList(); List<Integer ...
- Java fork join ForkJoinPool 用法例子
本例是把一个大的数组求和的计算的大任务分解到在小范围内求和的小任务,然后把这些小任务之和加起来就是所求之结果. 技术:JDK8.0, Javafork-join模式下的RecursiveTask技术, ...
- Java并发——Fork/Join框架
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...
- 《java.util.concurrent 包源码阅读》22 Fork/Join框架的初体验
JDK7引入了Fork/Join框架,所谓Fork/Join框架,个人解释:Fork分解任务成独立的子任务,用多线程去执行这些子任务,Join合并子任务的结果.这样就能使用多线程的方式来执行一个任务. ...
- 《java.util.concurrent 包源码阅读》24 Fork/Join框架之Work-Stealing
仔细看了Doug Lea的那篇文章:A Java Fork/Join Framework 中关于Work-Stealing的部分,下面列出该算法的要点(基本是原文的翻译): 1. 每个Worker线程 ...
随机推荐
- Java 提效神器 Stream 的冷门技巧
Stream 使用这个方法创建一个 Stream 对象. new ArrayList<>().stream() Filter 过滤器,里面传递一个函数,这个函数的返回结果如果为 true ...
- 重新点亮linux 命令树————帮助命令[一]
前言 重新整理一下linux的命令. 正文 这里首先介绍帮助命令. 帮助命令常用的有三个: man help info 那么就来看下这三个. man 第一个man,man不是男人的意思,而是manua ...
- Vue3全家桶升级指南二ref、toRef、toRefs的区别
ref是对原始数据的拷贝,当修改ref数据时,模板中的视图会发生改变,但是原始数据并不会改变. toRef是对原始数据的引用,修改toRef数据时,原始数据也会发生改变,但是视图并不会更新. 在vue ...
- 13、windows下卸载oracle
13.1.停用oracle服务: 进入计算机管理,在服务中,找到oracle开头的所有服务,右击选择停止: 13.2.删除oracle: 在开始菜单中,找到oracle->Universal I ...
- 修改MySQL时区
说明: 1.Windows版本暂无发现问题 2.CentOS-Docker版本需要修改时区 通过sql命令临时修改 mysql> set global time_zone = '+8:00'; ...
- CentOS-Docker安装phpMyAdmin(MySQL的web客户端)
注:phpMyAdmin是一款MySQL的web客户端,还是很直观且好用的,所以推荐给你~ 下载镜像 $ docker pull phpmyadmin/phpmyadmin 运行镜像 1.无指定固定数 ...
- SpringCloud:扩展zuul配置路由访问
继续上次整合SpringCloud的demo进行扩展zuul:https://www.cnblogs.com/nhdlb/p/12555968.html 这里我把zuul划分出一个模块单独启动 创建 ...
- Linux中grep和egrep命令详解
rep / egrep 语法: grep [-cinvABC] 'word' filename -c :打印符合要求的行数-i :忽略大小写-n :在输出符合要求的行的同时连同行号一起输出-v ...
- buu signin
一.拖入ida,来静态分析F5大法好 要注意的点: 1._gmz_init_set_str() 这个函数,也是看师傅的wp,学到的,以后还是得多google, 本质上是这个函数: int mpz_in ...
- TestComplete 64位和32位之间的区别
在64位系统上,有两种版本的TestComplete:32位和64位.本主题描述了TestComplete x64及其32位版本之间的区别.关于TestComplete x64启动TestComple ...