现代的计算机已经向多CPU方向发展,即使是普通的PC,甚至现在的智能手机、多核处理器已被广泛应用。在未来,处理器的核心数将会发展的越来越多。

虽然硬件上的多核CPU已经十分成熟,但是很多应用程序并未这种多核CPU做好准备,因此并不能很好地利用多核CPU的性能优势。

为了充分利用多CPU、多核CPU的性能优势,级软基软件系统应该可以充分“挖掘”每个CPU的计算能力,决不能让某个CPU处于“空闲”状态。为此,可以考虑把一个任务拆分成多个“小任务”,把多个"小任务"放到多个处理器核心上并行执行。当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。

如下面的示意图所示:

第一步分割任务。首先我们需要有一个fork类来把大任务分割成子任务,有可能子任务还是很大,所以还需要不停的分割,直到分割出的子任务足够小。

第二步执行任务并合并结果。分割的子任务分别放在双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里,启动一个线程从队列里拿数据,然后合并这些数据。

Java提供了ForkJoinPool来支持将一个任务拆分成多个“小任务”并行计算,再把多个“小任务”的结果合成总的计算结果。

ForkJoinPool是ExecutorService的实现类,因此是一种特殊的线程池。ForkJoinPool提供了如下两个常用的构造器。

  • public ForkJoinPool(int parallelism):创建一个包含parallelism个并行线程的ForkJoinPool
  • public ForkJoinPool() :以Runtime.getRuntime().availableProcessors()的返回值作为parallelism来创建ForkJoinPool

创建ForkJoinPool实例后,可以钓鱼ForkJoinPool的submit(ForkJoinTask<T> task)或者invoke(ForkJoinTask<T> task)来执行指定任务。其中ForkJoinTask代表一个可以并行、合并的任务。ForkJoinTask是一个抽象类,它有两个抽象子类:RecursiveAction和RecursiveTask。

  • RecursiveTask代表有返回值的任务
  • RecursiveAction代表没有返回值的任务。

一、RecursiveAction

下面以一个没有返回值的大任务为例,介绍一下RecursiveAction的用法。

大任务是:打印0-200的数值。

小任务是:每次只能打印50个数值。

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.TimeUnit; //RecursiveAction为ForkJoinTask的抽象子类,没有返回值的任务
class PrintTask extends RecursiveAction {
// 每个"小任务"最多只打印50个数
private static final int MAX = 50; private int start;
private int end; PrintTask(int start, int end) {
this.start = start;
this.end = end;
} @Override
protected void compute() {
// 当end-start的值小于MAX时候,开始打印
if ((end - start) < MAX) {
for (int i = start; i < end; i++) {
System.out.println(Thread.currentThread().getName() + "的i值:"
+ i);
}
} else {
// 将大任务分解成两个小任务
int middle = (start + end) / 2;
PrintTask left = new PrintTask(start, middle);
PrintTask right = new PrintTask(middle, end);
// 并行执行两个小任务
left.fork();
right.fork();
}
}
} public class ForkJoinPoolTest {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();
// 提交可分解的PrintTask任务
forkJoinPool.submit(new PrintTask(0, 200));
forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
// 关闭线程池
forkJoinPool.shutdown();
} }

运行结果如下:

ForkJoinPool-1-worker-2的i值:75
ForkJoinPool-1-worker-2的i值:76
ForkJoinPool-1-worker-2的i值:77
ForkJoinPool-1-worker-2的i值:78
ForkJoinPool-1-worker-2的i值:79
ForkJoinPool-1-worker-2的i值:80
ForkJoinPool-1-worker-2的i值:81
ForkJoinPool-1-worker-2的i值:82
ForkJoinPool-1-worker-2的i值:83
ForkJoinPool-1-worker-2的i值:84
ForkJoinPool-1-worker-2的i值:85
ForkJoinPool-1-worker-2的i值:86
ForkJoinPool-1-worker-2的i值:87
ForkJoinPool-1-worker-2的i值:88
ForkJoinPool-1-worker-2的i值:89
ForkJoinPool-1-worker-2的i值:90
ForkJoinPool-1-worker-2的i值:91
ForkJoinPool-1-worker-2的i值:92
ForkJoinPool-1-worker-2的i值:93
ForkJoinPool-1-worker-2的i值:94
ForkJoinPool-1-worker-2的i值:95
ForkJoinPool-1-worker-2的i值:96
ForkJoinPool-1-worker-2的i值:97
ForkJoinPool-1-worker-2的i值:98
ForkJoinPool-1-worker-2的i值:99
ForkJoinPool-1-worker-2的i值:50
ForkJoinPool-1-worker-2的i值:51
ForkJoinPool-1-worker-2的i值:52
ForkJoinPool-1-worker-2的i值:53
ForkJoinPool-1-worker-2的i值:54
ForkJoinPool-1-worker-2的i值:55
ForkJoinPool-1-worker-2的i值:56
ForkJoinPool-1-worker-2的i值:57
ForkJoinPool-1-worker-2的i值:58
ForkJoinPool-1-worker-2的i值:59
ForkJoinPool-1-worker-2的i值:60
ForkJoinPool-1-worker-2的i值:61
ForkJoinPool-1-worker-2的i值:62
ForkJoinPool-1-worker-2的i值:63
ForkJoinPool-1-worker-2的i值:64
ForkJoinPool-1-worker-2的i值:65
ForkJoinPool-1-worker-2的i值:66
ForkJoinPool-1-worker-2的i值:67
ForkJoinPool-1-worker-2的i值:68
ForkJoinPool-1-worker-2的i值:69
ForkJoinPool-1-worker-1的i值:175
ForkJoinPool-1-worker-1的i值:176
ForkJoinPool-1-worker-1的i值:177
ForkJoinPool-1-worker-1的i值:178
ForkJoinPool-1-worker-1的i值:179
ForkJoinPool-1-worker-1的i值:180
ForkJoinPool-1-worker-1的i值:181
ForkJoinPool-1-worker-1的i值:182
ForkJoinPool-1-worker-1的i值:183
ForkJoinPool-1-worker-1的i值:184
ForkJoinPool-1-worker-1的i值:185
ForkJoinPool-1-worker-1的i值:186
ForkJoinPool-1-worker-1的i值:187
ForkJoinPool-1-worker-1的i值:188
ForkJoinPool-1-worker-1的i值:189
ForkJoinPool-1-worker-1的i值:190
ForkJoinPool-1-worker-1的i值:191
ForkJoinPool-1-worker-1的i值:192
ForkJoinPool-1-worker-1的i值:193
ForkJoinPool-1-worker-1的i值:194
ForkJoinPool-1-worker-1的i值:195
ForkJoinPool-1-worker-1的i值:196
ForkJoinPool-1-worker-1的i值:197
ForkJoinPool-1-worker-1的i值:198
ForkJoinPool-1-worker-1的i值:199
ForkJoinPool-1-worker-1的i值:150
ForkJoinPool-1-worker-1的i值:151
ForkJoinPool-1-worker-1的i值:152
ForkJoinPool-1-worker-1的i值:153
ForkJoinPool-1-worker-1的i值:154
ForkJoinPool-1-worker-1的i值:155
ForkJoinPool-1-worker-1的i值:156
ForkJoinPool-1-worker-1的i值:157
ForkJoinPool-1-worker-1的i值:158
ForkJoinPool-1-worker-1的i值:159
ForkJoinPool-1-worker-1的i值:160
ForkJoinPool-1-worker-1的i值:161
ForkJoinPool-1-worker-1的i值:162
ForkJoinPool-1-worker-1的i值:163
ForkJoinPool-1-worker-1的i值:164
ForkJoinPool-1-worker-1的i值:165
ForkJoinPool-1-worker-1的i值:166
ForkJoinPool-1-worker-1的i值:167
ForkJoinPool-1-worker-1的i值:168
ForkJoinPool-1-worker-1的i值:169
ForkJoinPool-1-worker-1的i值:170
ForkJoinPool-1-worker-1的i值:171
ForkJoinPool-1-worker-1的i值:172
ForkJoinPool-1-worker-1的i值:173
ForkJoinPool-1-worker-1的i值:174
ForkJoinPool-1-worker-1的i值:125
ForkJoinPool-1-worker-1的i值:126
ForkJoinPool-1-worker-1的i值:127
ForkJoinPool-1-worker-1的i值:128
ForkJoinPool-1-worker-1的i值:129
ForkJoinPool-1-worker-1的i值:130
ForkJoinPool-1-worker-1的i值:131
ForkJoinPool-1-worker-1的i值:132
ForkJoinPool-1-worker-1的i值:133
ForkJoinPool-1-worker-1的i值:134
ForkJoinPool-1-worker-1的i值:135
ForkJoinPool-1-worker-1的i值:136
ForkJoinPool-1-worker-1的i值:137
ForkJoinPool-1-worker-1的i值:138
ForkJoinPool-1-worker-1的i值:139
ForkJoinPool-1-worker-1的i值:140
ForkJoinPool-1-worker-1的i值:141
ForkJoinPool-1-worker-1的i值:142
ForkJoinPool-1-worker-1的i值:143
ForkJoinPool-1-worker-1的i值:144
ForkJoinPool-1-worker-1的i值:145
ForkJoinPool-1-worker-1的i值:146
ForkJoinPool-1-worker-1的i值:147
ForkJoinPool-1-worker-1的i值:148
ForkJoinPool-1-worker-1的i值:149
ForkJoinPool-1-worker-1的i值:100
ForkJoinPool-1-worker-1的i值:101
ForkJoinPool-1-worker-1的i值:102
ForkJoinPool-1-worker-1的i值:103
ForkJoinPool-1-worker-1的i值:104
ForkJoinPool-1-worker-1的i值:105
ForkJoinPool-1-worker-1的i值:106
ForkJoinPool-1-worker-1的i值:107
ForkJoinPool-1-worker-1的i值:108
ForkJoinPool-1-worker-1的i值:109
ForkJoinPool-1-worker-1的i值:110
ForkJoinPool-1-worker-1的i值:111
ForkJoinPool-1-worker-1的i值:112
ForkJoinPool-1-worker-1的i值:113
ForkJoinPool-1-worker-1的i值:114
ForkJoinPool-1-worker-1的i值:115
ForkJoinPool-1-worker-1的i值:116
ForkJoinPool-1-worker-1的i值:117
ForkJoinPool-1-worker-1的i值:118
ForkJoinPool-1-worker-1的i值:119
ForkJoinPool-1-worker-1的i值:120
ForkJoinPool-1-worker-1的i值:121
ForkJoinPool-1-worker-1的i值:122
ForkJoinPool-1-worker-1的i值:123
ForkJoinPool-1-worker-1的i值:124
ForkJoinPool-1-worker-1的i值:25
ForkJoinPool-1-worker-1的i值:26
ForkJoinPool-1-worker-1的i值:27
ForkJoinPool-1-worker-1的i值:28
ForkJoinPool-1-worker-1的i值:29
ForkJoinPool-1-worker-1的i值:30
ForkJoinPool-1-worker-1的i值:31
ForkJoinPool-1-worker-1的i值:32
ForkJoinPool-1-worker-1的i值:33
ForkJoinPool-1-worker-1的i值:34
ForkJoinPool-1-worker-1的i值:35
ForkJoinPool-1-worker-1的i值:36
ForkJoinPool-1-worker-1的i值:37
ForkJoinPool-1-worker-1的i值:38
ForkJoinPool-1-worker-1的i值:39
ForkJoinPool-1-worker-1的i值:40
ForkJoinPool-1-worker-1的i值:41
ForkJoinPool-1-worker-1的i值:42
ForkJoinPool-1-worker-1的i值:43
ForkJoinPool-1-worker-1的i值:44
ForkJoinPool-1-worker-1的i值:45
ForkJoinPool-1-worker-1的i值:46
ForkJoinPool-1-worker-1的i值:47
ForkJoinPool-1-worker-1的i值:48
ForkJoinPool-1-worker-1的i值:49
ForkJoinPool-1-worker-1的i值:0
ForkJoinPool-1-worker-1的i值:1
ForkJoinPool-1-worker-1的i值:2
ForkJoinPool-1-worker-1的i值:3
ForkJoinPool-1-worker-1的i值:4
ForkJoinPool-1-worker-1的i值:5
ForkJoinPool-1-worker-1的i值:6
ForkJoinPool-1-worker-1的i值:7
ForkJoinPool-1-worker-1的i值:8
ForkJoinPool-1-worker-1的i值:9
ForkJoinPool-1-worker-1的i值:10
ForkJoinPool-1-worker-1的i值:11
ForkJoinPool-1-worker-1的i值:12
ForkJoinPool-1-worker-1的i值:13
ForkJoinPool-1-worker-1的i值:14
ForkJoinPool-1-worker-1的i值:15
ForkJoinPool-1-worker-1的i值:16
ForkJoinPool-1-worker-1的i值:17
ForkJoinPool-1-worker-1的i值:18
ForkJoinPool-1-worker-1的i值:19
ForkJoinPool-1-worker-1的i值:20
ForkJoinPool-1-worker-1的i值:21
ForkJoinPool-1-worker-1的i值:22
ForkJoinPool-1-worker-1的i值:23
ForkJoinPool-1-worker-1的i值:24
ForkJoinPool-1-worker-2的i值:70
ForkJoinPool-1-worker-2的i值:71
ForkJoinPool-1-worker-2的i值:72
ForkJoinPool-1-worker-2的i值:73
ForkJoinPool-1-worker-2的i值:74

从上面结果来看,ForkJoinPool启动了两个线程来执行这个打印任务,这是因为笔者的计算机的CPU是双核的。不仅如此,读者可以看到程序虽然打印了0-199这两百个数字,但是并不是连续打印的,这是因为程序将这个打印任务进行了分解,分解后的任务会并行执行,所以不会按顺序从0打印 到199。

二、RecursiveTask

下面以一个有返回值的大任务为例,介绍一下RecursiveTask的用法。

大任务是:计算随机的100个数字的和。

小任务是:每次只能20个数值的和。

import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask; //RecursiveTask为ForkJoinTask的抽象子类,有返回值的任务
class SumTask extends RecursiveTask<Integer> {
// 每个"小任务"最多只打印50个数
private static final int MAX = 20;
private int arr[];
private int start;
private int end; SumTask(int arr[], int start, int end) {
this.arr = arr;
this.start = start;
this.end = end;
} @Override
protected Integer compute() {
int sum = 0;
// 当end-start的值小于MAX时候,开始打印
if ((end - start) < MAX) {
for (int i = start; i < end; i++) {
sum += arr[i];
}
return sum;
} else {
System.err.println("=====任务分解======");
// 将大任务分解成两个小任务
int middle = (start + end) / 2;
SumTask left = new SumTask(arr, start, middle);
SumTask right = new SumTask(arr, middle, end);
// 并行执行两个小任务
left.fork();
right.fork();
// 把两个小任务累加的结果合并起来
return left.join() + right.join();
}
} } public class ForkJoinPoolTest2 {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int arr[] = new int[100];
Random random = new Random();
int total = 0;
// 初始化100个数字元素
for (int i = 0; i < arr.length; i++) {
int temp = random.nextInt(100);
// 对数组元素赋值,并将数组元素的值添加到total总和中
total += (arr[i] = temp);
}
System.out.println("初始化时的总和=" + total);
// 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
ForkJoinPool forkJoinPool = new ForkJoinPool();
// 提交可分解的PrintTask任务
Future<Integer> future = forkJoinPool.submit(new SumTask(arr, 0,
arr.length));
System.out.println("计算出来的总和=" + future.get());
// 关闭线程池
forkJoinPool.shutdown();
} }

计算结果如下:

初始化时的总和=4283
=====任务分解======
=====任务分解======
=====任务分解======
=====任务分解======
=====任务分解======
=====任务分解======
=====任务分解======
计算出来的总和=4283

从上面结果来看,ForkJoinPool将任务分解了7次,程序通过SumTask计算出来的结果,和初始化数组时统计出来的总和是相等的,这表明计算结果一切正常。

读者还参考以下文章加深对ForkJoinPool的理解:

http://www.infoq.com/cn/articles/fork-join-introduction/

http://www.ibm.com/developerworks/cn/java/j-lo-forkjoin/

==================================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址:http://blog.csdn.net/ouyang_peng

==================================================================================================

我的Java开发学习之旅------>Java使用Fork/Join框架来并行执行任务的更多相关文章

  1. 我的Java开发学习之旅------>Java 格式化类(java.util.Formatter)基本用法

    本文参考: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Formatter.html http://www.blogjava.net/ ...

  2. 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

    今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...

  3. 我的Java开发学习之旅------>Java使用ObjectOutputStream和ObjectInputStream序列号对象报java.io.EOFException异常的解决方法

    今天用ObjectOutputStream和ObjectInputStream进行对象序列化话操作的时候,报了java.io.EOFException异常. 异常代码如下: java.io.EOFEx ...

  4. 我的Java开发学习之旅------>Java利用Comparator接口对多个排序条件进行处理

    一需求 二实现Comparator接口 三验证排序结果 验证第一条件首先按级别排序级别最高的排在前面 验证第二条如果级别相等那么按工资排序工资高的排在前面 验证第三条如果工资相当则按入职年数排序入职时 ...

  5. 我的Java开发学习之旅------>Java String对象作为参数传递的问题解惑

    又是一道面试题,来测试你的Java基础是否牢固. 题目:以下代码的运行结果是? public class TestValue { public static void test(String str) ...

  6. 我的Java开发学习之旅------>Java语言中方法的参数传递机制

    实参:如果声明方法时包含来了形参声明,则调用方法时必须给这些形参指定参数值,调用方法时传给形参的参数值也被称为实参. Java的实参值是如何传入方法?这是由Java方法的参数传递机制来控制的,Java ...

  7. 我的Java开发学习之旅------>Java经典排序算法之归并排序

    一.归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序列 ...

  8. 我的Java开发学习之旅------>Java经典排序算法之快速排序

    一.算法思想     快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod).(1) 分治法的 ...

  9. 我的Java开发学习之旅------>Java经典排序算法之二分插入排序

    一.折半插入排序(二分插入排序) 将直接插入排序中寻找A[i]的插入位置的方法改为采用折半比较,即可得到折半插入排序算法.在处理A[i]时,A[0]--A[i-1]已经按关键码值排好序.所谓折半比较, ...

随机推荐

  1. centos 7 关闭firewalld开启iptables

    1: 关闭系统高级防火墙firewalld systemctl stop firewalld.service #停止firewall systemctl disable firewalld.servi ...

  2. goreplay使用

    最新版的发布公告:https://leonsbox.com/goreplay-v0-16-and-4th-anniversary-5408b1fd72e0 主要提到:中间件.报文解压.从kafka读取 ...

  3. 细数国外SEO,SEM,SNS资深博客论坛和站点

    如果你有时间,如果有英语还不错能看懂国外的推广营销知识,如果你想做个实战者,如果你想比别人多领先,如果你爱好这个推广行业,如果你不想做河塘里的小鱼,如果····请一个个的看以下的站点,个人觉得会给你另 ...

  4. 转: Syslog协议介绍

    转: http://liu-hliang.iteye.com/blog/827392 在网上搜的文章,写的很全乎.摘抄如下,供大家参考学习 1.介绍 在Unix类操作系统上,syslog广泛应用于系统 ...

  5. Tomcat的Manager显示403 Access Denied

    管理tomcat的时候遇到了以下问题: 1.刚开始需要用户名密码,不知道用户名和密码是什么,但是输入什么都不正确. 解决办法: 自己在tomcat-users.xml中按格式添加用户 conf文件夹里 ...

  6. C端端口扫描工具,发现www服务

    作者d_m 简述 起初是90sec一个帖子中的代码(见参考[1]),看了之后感觉很不错.当然工作内容nmap也可以完成.不过在实际比较后决定还是用python脚本完成. 优化 所以也算是很久以前的一个 ...

  7. javascript---》Fcuntion对象

    Function 对象的valueOf() 和 toString() 方法.返回函数的源代码,调试时有用 Function 对象的 length 属性返回函数期望的参数个数------>接受任意 ...

  8. Linux视频培训教程

    很详尽的Linux培训教程,既包含日常工作常常要用到的实践及技巧,又包含Linux认证及系统管理及架构,讲的很不错.最关键的.这么具体,完整的教程还是免费的.花了点时间拿它整理了下. 第一部分: Li ...

  9. 浅谈对TDD的看法

    程序猿对TDD这个词一定不陌生.近几年比較火.英文全称Test-Driven Development,測试驱动开发. 它要求在编写某个功能的代码之前先编写測试代码,然后仅仅编写使測试通过的功能代码,通 ...

  10. (十)jQuery对表单、表格的操作

    一.表单应用 1.HTML中的表单大致由三部分组成 (1).表单标签:包含处理表单数据所用的服务端程序URL,以及数据提交到服务器的方法. (2).表单域:包含文本框.密码框.隐藏域.多行文本框.复选 ...