【java并发核心八】Fork-Join分治编程
jdk1.7中提供了Fork/Join并行执行任务框架,主要作用就是把大任务分割成若干个小任务,再对每个小任务得到的结果进行汇总。

正常情况下,一些小任务我们可以使用单线程递归来实现,但是如果要想充分利用CPU资源,就需要把一个任务分成若干个小任务,并行执行了,这就是分治编程。
在JDK中,并行执行框架Fork-Join使用了“工作窃取(work-stealing)”算法。
JDK1.7中实现分治编程思路:
使用 ForkJoinPool 类提供了一个任务池。
具体执行任务需要靠 ForkJoinTask 类,而 ForkJoinTask 是抽象类,故使用该类的3个子类 CountedCompleter、RecursiveAction、RecursiveTask 来实现具体的功能。
其中,RecursiveAction 执行的任务具有无返回值,且仅执行一次;RecursiveTask 执行任务可以通过方法 join() 或者 get() 取得方法返回值。
补充, join() 和 get() 的区别:两个方法都可以获得计算后的结果值,区别是在子任务报异常时,get() 的异常可以在main主线程中进行捕获;而 join() 的异常会直接抛出。
看例子(求和:1+2+3+...+9999+10000):
package com.cd.thread; import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask; public class ForkJoinTest {
public static void main(String[] args) {
final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
System.out.println("start-time:" + sf.format(new Date()));
addTest(); // for循环
System.out.println("end-time:" + sf.format(new Date()));
System.out.println("start-time:" + sf.format(new Date()));
forkJoinTest();// 分治求和
System.out.println("end-time:" + sf.format(new Date()));
} private static void addTest() {
int beginNum = 1, endNum = 10000, val = 0;
for (int i = beginNum; i <= endNum; i++) {
val += i;
}
System.out.println("for循环结果:" + val);
} public static void forkJoinTest() {
MyRecursiveTask task = new MyRecursiveTask(1, 10000);
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Integer> fjTask = pool.submit(task);
try {
System.out.println("分治求和结果:" + fjTask.get());
} catch (Exception e) {
e.printStackTrace();
}
} private static class MyRecursiveTask extends RecursiveTask<Integer> { private Integer beginNum; private Integer endNum; private MyRecursiveTask(Integer beginNum, Integer endNum) {
this.beginNum = beginNum;
this.endNum = endNum;
} @Override
protected Integer compute() {
if ((endNum - beginNum) > 500) {
int middleNum = (endNum + beginNum) / 2;
MyRecursiveTask task1 = new MyRecursiveTask(beginNum, middleNum);
MyRecursiveTask task2 = new MyRecursiveTask(middleNum + 1, endNum);
this.invokeAll(task1, task2);
// return task1.join() + task2.join();
Integer num1 = 0, num2 = 0;
try {
num1 = task1.get();
num2 = task2.get();
} catch (Exception e) {
e.printStackTrace();
}
return num1 + num2;
} else {
Integer val = 0;
for (int i = beginNum; i <= endNum; i++) {
val += i;
}
return val;
}
} } }

从结果看,分治编程不一定会比单线程快,所以在用分治编程的时候,需要一定的测试才行。
而分治编程也有运用的领域,比如遍历一个目录及其子目录,处理一个树形结构算法问题。
在写代码的时候,会发现分治的代码看起来像递归,但是其实它们是并行执行的。
关于 ForkJoinPool 的 api,建议用到的时候,去看文档吧,看文档也是一种能力,也是一种技巧。
【java并发核心八】Fork-Join分治编程的更多相关文章
- 【java并发系列】Fork/Join任务(转)
原文链接 当我们需要执行大量的小任务时,有经验的Java开发人员都会采用线程池来高效执行这些小任务.然而,有一种任务,例如,对超过1000万个元素的数组进行排序,这种任务本身可以并发执行,但如何拆解成 ...
- JAVA并发工具类---------------(Fork/Join)
Fork/Join 分而治之 将一个大任务分成数个小任务执行,然后将这些小人物执行后的结果进行join汇总: (假设:你要计算1到1000的总和,你可以把它分成1-100,101-200,...... ...
- 【Java并发核心三】CountDownLatch、CyclicBarrier及Phaser
个人感觉,看书学习还是需要“不求甚解”,因为一旦太过于计较小的得失,就容易钻牛角尖,学习进度也慢.我们完全可以先学一个大概,等到真正用到的时候再把那些细节丰富起来,就更有针对性. 所以,针对java并 ...
- 并发编程之Fork/Join
并发与并行 并发:多个进程交替执行. 并行:多个进程同时进行,不存在线程的上下文切换. 并发与并行的目的都是使CPU的利用率达到最大.Fork/Join就是为了尽可能提高硬件的使用率而应运而生的. 计 ...
- JAVA并行框架:Fork/Join
一.背景 虽然目前处理器核心数已经发展到很大数目,但是按任务并发处理并不能完全充分的利用处理器资源,因为一般的应用程序没有那么多的并发处理任务.基于这种现状,考虑把一个任务拆分成多个单元,每个单元分别 ...
- 【java并发核心一】Semaphore 的使用思路
最近在看一本书<Java并发编程 核心方法与框架>,打算一边学习一边把学习的经验记下来,所粘贴的代码都是我运行过的,大家一起学习,欢迎吐槽. 估计也没多少人看我的博客,哈哈,那么我还是会记 ...
- Java并发(八)计算线程池最佳线程数
目录 一.理论分析 二.实际应用 为了加快程序处理速度,我们会将问题分解成若干个并发执行的任务.并且创建线程池,将任务委派给线程池中的线程,以便使它们可以并发地执行.在高并发的情况下采用线程池,可以有 ...
- java成神之——Fork/Join基本使用
Fork/Join 大任务分小任务,小任务结果合并 ForkJoinPool pool = new ForkJoinPool(); RecursiveTask<Integer> task1 ...
- Java并发(八):AbstractQueuedSynchronizer
先做总结: 1.AbstractQueuedSynchronizer是什么? AbstractQueuedSynchronizer(AQS)这个抽象类,是Java并发包 java.util.concu ...
随机推荐
- libopencv_highgui.so.2.4.9:对‘TIFFReadRGBAStrip@LIBTIFF_4.0’未定义的引用
make之前加上sudo su重新make即可 http://blog.csdn.net/cfyzcc/article/details/52981467
- Python计算器实操
要求: 开发一个简单的python计算器 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * ...
- kali linux宿主机和虚拟机互访实现方案
1.攻防模拟中,将DVWA安装到自己的宿主机中,在kali Linux中通过sqlmap和其他工具启动嗅探攻击,需要配置网络.虚拟机采用桥接方式,并复制Mac地址状况. 2.查看各自系统下的IP地址. ...
- Allegro PCB Design GXL (legacy) 刷新PCB封装(Package)中的焊盘(Padstack)
Allegro PCB Design GXL (legacy) version 16.6-2015 “人有失足,马有失蹄”. 像这个电位器的封装的Pin 6,在制作Padstack时,因没有添加SOL ...
- Loadrunner 接口依赖测试
Action() { //利用关联获取第一个GET请求的返回XXX字段的值,并存储到response_XXX变量中. web_reg_save_param_ex( "ParamName=re ...
- 引用的作用&引用与指针的区别
引入 C语言中函数有两种传参的方式: 传值和传址.以传值方式, 在函数调用过程中会生成一份临时变量用形参代替, 最终把实参的值传递给新分配的临时变量即形参. 它的优点是避免了函数调用的一些副作用, 但 ...
- 该问题是需要导包!!!需要pom中添加依赖The absolute uri: http://java.sun.com/jsp/jstl/core cannot be resolved in either web.xml or the jar files deployed with this application
<!-- https://mvnrepository.com/artifact/org.apache.taglibs/taglibs-standard-impl --><depend ...
- 如果IDEA右上角的tomcat消失了,解决办法
看了很多博客都没有找到解决办法,还是老师帮我解决的
- .Net分布式锁
项目中一般使用lock作为锁,以便于多线程操作确保库内数据统一.但是如果分布式部署项目,则这种锁就是去了意义,这时可以使用redis或memcache的add方法作为分布式锁. 栗子
- CentOS升级glibc-2.14
升级glibc-2.14用到的rpm https://pan.baidu.com/s/1v-Uk579TGM6498cExst6ow 先要安装gcc yum -y install gcc 执行: rp ...