Java并行任务框架Fork/Join
Fork/Join是什么?
Fork意思是分叉,Join为合并。Fork/Join是一个将任务分割并行运行,然后将最终结果合并成为大任务的结果的框架,父任务可以分割成若干个子任务,子任务可以继续分割,提供我们一种方便的并行任务功能,满足实际场景的业务需求,思想类似于MapReduce。任务的分割必须保证子任务独立,不会相互依赖结果。

从哪里开始?
Fork/Join框架主要有如下接口和类:
- ForkJoinPool:一个线程池,用于执行调度分割的任务,实现了ExecutorService接口。提供三种执行任务的方式:
1、execute:最原生的执行方式,以异步执行,并且无返回结果。
2、submit:异步执行,有返回结果,返回结果是封装后的Future对象。
3、invoke和invokeAll:异步执行,有返回结果,会等待所有任务执行执行完成,返回的结果为无封装的泛型T。
- ForkJoinTask:抽象的分割任务,提供以分叉的方式执行,以及合并执行结果。
- RecursiveAction:异步任务,无返回结果。通常自定义的任务要继承,并重写compute方法,任务执行的就是compute方法。
- RecursiveTask:异步任务,有返回结果。通常自定义的任务要继承,并重写compute方法,任务执行的就是compute方法。
核心类图

从核心类图看出,要想开始一个分割的并行任务,可以创建一个ForkJoinPool线程池,同时创建无返回结果的任务RecursiveAction或有返回结果的任务RecursiveTask,最后调用线程池ForkJoinPool的execute或submit或invoke方法执行任务,完成后合并结果。
实例
我们以一个有返回结果的并行任务实例进行测试。计算从起始值到结束值得连续数的累加结果,利用Fork/Join框架。并对比普通计算和并行计算的耗时差异。
package com.misout.forkjoin;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
/**
* 计算从起始值到结束值得连续数的累加结果,利用Fork/Join框架
* @author Misout
* @date 2018-01-13 16:06:44
*/
public class SumTask extends RecursiveTask<Long> {
private static final long serialVersionUID = 4828818665955149519L;
/** 每个任务最多允许计算的数字个数阈值,超过这个阈值,任务进行拆分 */
private static final long THRESHOLD = 1000L;
/** 起始值 */
private Long startNumber;
/** 结束值 */
private Long endNumber;
public SumTask(Long startNumber, Long endNumber) {
this.startNumber = startNumber;
this.endNumber = endNumber;
}
/**
* 累加数的个数超过阈值1000个,拆分成2个子任务执行。子任务继续作拆分。计算完,合并结果。
*/
@Override
protected Long compute() {
if(startNumber > endNumber) {
System.out.println("start number should be smaller than end number");
return 0L;
}
if(endNumber - startNumber < THRESHOLD) {
return this.getCount(startNumber, endNumber);
} else {
Long mid = (startNumber + endNumber) / 2;
RecursiveTask<Long> subTask1 = new SumTask(startNumber, mid);
RecursiveTask<Long> subTask2 = new SumTask(mid + 1, endNumber);
subTask1.fork();
subTask2.fork();
return subTask1.join() + subTask2.join();
}
}
/**
* 普通累加执行方法
* @param start 起始数
* @param end 结束数
* @return 累加和
*/
protected Long getCount(Long start, Long end) {
Long sum = 0L;
for(long i = start; i <= end; i++) {
sum += i;
}
return sum;
}
public static void main(String[] args) {
ForkJoinPool forkJoinPool = new ForkJoinPool();
Long start = 5L;
Long end = 3463434L;
SumTask task = new SumTask(start, end);
Long startTime = System.currentTimeMillis();
Long sum = forkJoinPool.invoke(task);
Long endTime = System.currentTimeMillis();
System.out.println("fork/join : sum = " + sum + ", cost time = " + (endTime - startTime) + "ms");
startTime = System.currentTimeMillis();
Long sum2 = task.getCount(start, end);
endTime = System.currentTimeMillis();
System.out.println("normal : sum = " + sum2 + ", cost time = " + (endTime - startTime) + "ms");
}
}
说明:SumTask继承RecursiveTask,并实现了compute方法。在compute方法中会进行任务分割,并继续生成子任务,子任务仍然以分割的方式运行。
运行结果对比:
fork/join : sum = 5997689267885, cost time = 290ms
normal : sum = 5997689267885, cost time = 41ms
注意事项:任务拆分的深度最好不要太多,否则很容易因创建的线程过多影响系统性能。
work-stealing规则
在Java的API说明中提到,ForkJoinPool线程池与ThreadPoolExecutor线程池不同的地方在于,ForkJoinPool善于利用窃取工作执行加快任务的总体执行速度。实际上,在ForkJoinPool线程池中,若一个工作线程的任务队列为空没有任务执行时,便从其他工作线程中获取任务主动执行。为了实现工作窃取,在工作线程中维护了双端队列,窃取任务线程从队尾获取任务,被窃取任务线程从队头获取任务。这种机制充分利用线程进行并行计算,减少了线程竞争。但是当队列中只存在一个任务了时,两个线程去取反而会造成资源浪费。

Java并行任务框架Fork/Join的更多相关文章
- JUC组件扩展(二)-JAVA并行框架Fork/Join(四):监控Fork/Join池
Fork/Join 框架是为了解决可以使用 divide 和 conquer 技术,使用 fork() 和 join() 操作把任务分成小块的问题而设计的.主要实现这个行为的是 ForkJoinPoo ...
- JUC组件扩展(二)-JAVA并行框架Fork/Join(二):同步和异步
在Fork/Join框架中,提交任务的时候,有同步和异步两种方式. invokeAll()的方法是同步的,也就是任务提交后,这个方法不会返回直到所有的任务都处理完了. fork方法是异步的.也就是你提 ...
- JUC组件扩展(二)-JAVA并行框架Fork/Join(一):简介和代码示例
一.背景 虽然目前处理器核心数已经发展到很大数目,但是按任务并发处理并不能完全充分的利用处理器资源,因为一般的应用程序没有那么多的并发处理任务.基于这种现状,考虑把一个任务拆分成多个单元,每个单元分别 ...
- JUC组件扩展(二)-JAVA并行框架Fork/Join(三):在任务中抛出异常
在java当中,异常一共分为两种.一种是运行时异常,一种是非运行是异常. 非运行时异常:这些异常必须在方法上通过throws子句抛出.或者在方法体内进行try{…}catch{…}来捕获异常. 运行时 ...
- Java 并发编程 -- Fork/Join 框架
概述 Fork/Join 框架是 Java7 提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.下图是网上流传的 Fork Join 的 ...
- java 中的fork join框架
文章目录 ForkJoinPool ForkJoinWorkerThread ForkJoinTask 在ForkJoinPool中提交Task java 中的fork join框架 fork joi ...
- Java 并发之 Fork/Join 框架
什么是 Fork/Join 框架 Fork/Join 框架是一种在 JDk 7 引入的线程池,用于并行执行把一个大任务拆成多个小任务并行执行,最终汇总每个小任务结果得到大任务结果的特殊任务.通过其命名 ...
- JAVA中的Fork/Join框架
看了下Java Tutorials中的fork/join章节,整理下. 什么是fork/join框架 fork/join框架是ExecutorService接口的一个实现,可以帮助开发人员充分利用多核 ...
- Java并发编程--Fork/Join框架使用
上篇博客我们介绍了通过CyclicBarrier使线程同步,可是上述方法存在一个问题,那就是假设一个大任务跑了2个线程去完毕.假设线程2耗时比线程1多2倍.线程1完毕后必须等待线程2完毕.等待的过程线 ...
随机推荐
- 整理之BroadcaseReceiver
广播的分类 有序广播:按接收器优先级从高到低接受消息,一次只能有一个接收器处理消息.中途可以被截断. 无序广播:所有接收器同时接受消息并处理,无法拦截. 本地广播:只能在本应用内传播的无需广播.上面两 ...
- Centos8.3安装broadcom(博通)BCM43142无线网卡驱动,Centos8没有wifi选项(No wifi adapter found centos)解决办法
参考:杆菌大祭司> https://www.jianshu.com/p/3cb41b7b8fec 第一步:查看网卡型号,确认无线网卡型号为BCMXXX lspci | grep Network ...
- C#使用异步需要注意的几个问题
C#异步使用需要注意的几个问题1.异步方法如果只是对别的方法的简单的转发调用,没哟复杂的逻辑(比如等待A的结果,再调用B,等待A调用的返回值拿到内部做一些处理再返回),那么就可以去掉async关键字. ...
- Aggressor Script 开发-Powershell 免杀
转载https://www.jianshu.com/p/f158a9d6bdcf 前言 在接触到Cobalt Strike的时候就知道有各种插件,想象着那天也可以自己学习编写一个.在之前分析Cobal ...
- React Native startReactApplication 方法简析
在 React Native 启动流程简析 这篇文章里,我们梳理了 RN 的启动流程,最后的 startReactApplication 由于相对复杂且涉及到最终执行前端 js 的流程,我们单独将其提 ...
- string类型数据的操作指令
1. 2. 3. 4. 5. 6. 7. 8. 9. 从右到左是索引从-1开始 10. 11. 12. 13. 14. 15.
- 使用 Dockerfile 自定义 Nginx 镜像
一般来说,自定义Nginx只需要把静态文件放到镜像里就可以了,不需要重写 CMD 与 ENTRYPOINT.但是,如果的确需要在 Nginx 启动前执行一些操作,就需要重写 CMD 了,如果写成下边就 ...
- Windos下通过Wpcap抓包实现两个网卡桥接
目录 1. 背景: 2. 需要的技术手段: 3. 实现逻辑: 4. 应用实例: 1. 背景: 一台电脑允许接多个网口,当然大部分只有一个网口其余都是USB扩展而来,而每个网口之间需要配置不同的网段IP ...
- Prometheus 2.21.0 新特性
Prometheus 2.21.0 现在(2020.09.11)已经发布,在上个月的 2.20.0 之后又进行了很多的修复和改进. 这个版本使用了 Go 1.15 进行编译,不赞成在TLS证书验证中使 ...
- 第一次用AngularJS
1.创建指令的4种方式(ECMA) var appModule = angular.module('app', []); appModule.directive('hello', function() ...