现代的计算机已经向多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个数值。

  1. import java.util.concurrent.ForkJoinPool;
  2. import java.util.concurrent.RecursiveAction;
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. //RecursiveAction为ForkJoinTask的抽象子类,没有返回值的任务
  6. class PrintTask extends RecursiveAction {
  7. // 每个"小任务"最多只打印50个数
  8. private static final int MAX = 50;
  9.  
  10. private int start;
  11. private int end;
  12.  
  13. PrintTask(int start, int end) {
  14. this.start = start;
  15. this.end = end;
  16. }
  17.  
  18. @Override
  19. protected void compute() {
  20. // 当end-start的值小于MAX时候,开始打印
  21. if ((end - start) < MAX) {
  22. for (int i = start; i < end; i++) {
  23. System.out.println(Thread.currentThread().getName() + "的i值:"
  24. + i);
  25. }
  26. } else {
  27. // 将大任务分解成两个小任务
  28. int middle = (start + end) / 2;
  29. PrintTask left = new PrintTask(start, middle);
  30. PrintTask right = new PrintTask(middle, end);
  31. // 并行执行两个小任务
  32. left.fork();
  33. right.fork();
  34. }
  35. }
  36. }
  37.  
  38. public class ForkJoinPoolTest {
  39. /**
  40. * @param args
  41. * @throws Exception
  42. */
  43. public static void main(String[] args) throws Exception {
  44. // 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
  45. ForkJoinPool forkJoinPool = new ForkJoinPool();
  46. // 提交可分解的PrintTask任务
  47. forkJoinPool.submit(new PrintTask(0, 200));
  48. forkJoinPool.awaitTermination(2, TimeUnit.SECONDS);//阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束
  49. // 关闭线程池
  50. forkJoinPool.shutdown();
  51. }
  52.  
  53. }

运行结果如下:

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

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

二、RecursiveTask

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

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

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

  1. import java.util.Random;
  2. import java.util.concurrent.ForkJoinPool;
  3. import java.util.concurrent.Future;
  4. import java.util.concurrent.RecursiveTask;
  5.  
  6. //RecursiveTask为ForkJoinTask的抽象子类,有返回值的任务
  7. class SumTask extends RecursiveTask<Integer> {
  8. // 每个"小任务"最多只打印50个数
  9. private static final int MAX = 20;
  10. private int arr[];
  11. private int start;
  12. private int end;
  13.  
  14. SumTask(int arr[], int start, int end) {
  15. this.arr = arr;
  16. this.start = start;
  17. this.end = end;
  18. }
  19.  
  20. @Override
  21. protected Integer compute() {
  22. int sum = 0;
  23. // 当end-start的值小于MAX时候,开始打印
  24. if ((end - start) < MAX) {
  25. for (int i = start; i < end; i++) {
  26. sum += arr[i];
  27. }
  28. return sum;
  29. } else {
  30. System.err.println("=====任务分解======");
  31. // 将大任务分解成两个小任务
  32. int middle = (start + end) / 2;
  33. SumTask left = new SumTask(arr, start, middle);
  34. SumTask right = new SumTask(arr, middle, end);
  35. // 并行执行两个小任务
  36. left.fork();
  37. right.fork();
  38. // 把两个小任务累加的结果合并起来
  39. return left.join() + right.join();
  40. }
  41. }
  42.  
  43. }
  44.  
  45. public class ForkJoinPoolTest2 {
  46. /**
  47. * @param args
  48. * @throws Exception
  49. */
  50. public static void main(String[] args) throws Exception {
  51. int arr[] = new int[100];
  52. Random random = new Random();
  53. int total = 0;
  54. // 初始化100个数字元素
  55. for (int i = 0; i < arr.length; i++) {
  56. int temp = random.nextInt(100);
  57. // 对数组元素赋值,并将数组元素的值添加到total总和中
  58. total += (arr[i] = temp);
  59. }
  60. System.out.println("初始化时的总和=" + total);
  61. // 创建包含Runtime.getRuntime().availableProcessors()返回值作为个数的并行线程的ForkJoinPool
  62. ForkJoinPool forkJoinPool = new ForkJoinPool();
  63. // 提交可分解的PrintTask任务
  64. Future<Integer> future = forkJoinPool.submit(new SumTask(arr, 0,
  65. arr.length));
  66. System.out.println("计算出来的总和=" + future.get());
  67. // 关闭线程池
  68. forkJoinPool.shutdown();
  69. }
  70.  
  71. }

计算结果如下:

  1. 初始化时的总和=4283
  2. =====任务分解======
  3. =====任务分解======
  4. =====任务分解======
  5. =====任务分解======
  6. =====任务分解======
  7. =====任务分解======
  8. =====任务分解======
  9. 计算出来的总和=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. UVA - 11774 Doom's Day

    看样例猜结论hhhhhh,竟然蒙对了..(正确性待证明) #include<bits/stdc++.h> #define ll long long using namespace std; ...

  2. shell实现自动备份整个数据库,一个库备份一个文件

    自动实现备份整个数据库 实现一个库备份一个文件 实现排除不需要备份的库 实现备份成压缩文件 实现定义保留多少天的备份文件 核心代码 #!/bin/bash #set -x ############## ...

  3. [置顶] django快速获取项目所有的URL

    django快速获取项目所有的URL django1.10快速获取项目所有的URL列表,可以用于权限控制 函数如下: import re def get_url(urllist , parent='' ...

  4. mac python 切换系统默认版本

    1 找到所安装python路径/usr/local/Cellar/python/2.7.13/bin2 vim ~/.bash_profile 3 添加如下代码: PATH="/usr/lo ...

  5. mac 配置sencha touch环境

    1 安装 java 2 安装 node js 为使用npm作准备 3 用npm命令安装 cordova npm install -g cordova

  6. ClassPathXmlApplicationContext和FileSystemXmlApplicationContext区别

    ClassPathXmlApplicationContext 默认文件路径是src下那一级classpath:和classpath*:的区别: classpath: 只能加载一个配置文件,如果配置了多 ...

  7. 2016.7.14 如何在浏览器中查看jsp文件

    参考资料: http://jingyan.baidu.com/article/ed15cb1b10f1241be36981ab.html 1.复制jsp文件地址 2.写在浏览器里 E:/lyh/tas ...

  8. Wireshark网络分析实战笔记(三)基本信息统计工具的使用方法

    Capture File Properties:获取网络中数据包的整体信息 用法:Statistics菜单条下Capture File Properties选项 Protocol Hierarchy: ...

  9. 通用礼品卡接口文档(KFC、必胜客、GAP等)

    通用礼品卡接口文档,集于各商家(KFC.必胜客.GAP等)实体卡和会员卡的API虚拟卡,可用于线上/下消费.移动支付. 1.API 1.1商品列表 接口地址:http://v.juhe.cn/gift ...

  10. iOS 自己定义页面的切换动画与交互动画 By Swift

    在iOS7之前,开发人员为了寻求自己定义Navigation Controller的Push/Pop动画,仅仅能受限于子类化一个UINavigationController,或是用自己定义的动画去覆盖 ...