现代的计算机已经向多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. workflow engine Ruote初体验之三(条件与美元符号)

    条件 我们可以用:if和:unless公共属性来进行条件判断,或者使用if,given,once或者equals(已经过时)关键字. 使用:if属性: 1 cursor do 2 participan ...

  2. 【windows】windows系统下,在任务管理器的进程选项卡中查看PID/任务管理器怎么查看PID

    PID,就是windows上的进程ID,是一个进程的唯一标识值. 那今天启动JDK跑起来一个项目之后,想要在任务管理器中查看这个JDK所在进程的PID但是看不到. 怎么解决? 1.我在任务管理器的服务 ...

  3. 网络库libevent、libev、libuv对比

    Libevent.libev.libuv三个网络库,都是c语言实现的异步事件库Asynchronousevent library). 异步事件库本质上是提供异步事件通知(Asynchronous Ev ...

  4. Geographical distance

    Introduction Calculating the distance between geographical coordinates is based on some level of abs ...

  5. mongodb分片片键的选择(持续更新中)

    首先要了解项目的情况,检查使用情况 对集合进行分片时,要选择一个或者两个字段拆分数据,这个键叫做片键 一旦拥有对个分片,在修改片键几乎是不肯能的事情,因此选择合适的片键是非常重要的. 对集合分片之前要 ...

  6. UNP学习笔记(第六章 I/O复用)

    I/O模型 首先我们将查看UNIX下可用的5种I/O模型的基本区别: 1.阻塞式I/O 2.非阻塞式I/O 3.I/O复用(select和poll) 4.信号驱动式I/O(SIGIO) 5.异步I/O ...

  7. 原生domReady封装

    核心思路: 标准浏览器(含IE9+)比较简单,直接监听DOMContentLoaded事件: 低版本的IE(IE678)两套机制: 1)尝试轮询document.documentElement.doS ...

  8. 让你十分钟学会shell

    1.先介绍下shell的工作原理 Shell可以被称作是脚本语言,因为它本身是不需要编译的,而是通过解释器解释之后再编译执行,和传统语言相比多了解释的过程所以效率会略差于传统的直接编译的语言. 这是s ...

  9. RFS+AutoItLibrary测试Web对话框

    Selenium2library在我们实际测试web页面的时候基本上已经够用了,不过还是会有部分情况下会脱离Selenium2library的控制,无法进行操作.比如说下载文件的时候,要选择保存文件在 ...

  10. 京东2015年应届生招聘笔试题(A)卷答案选择题部分

    1.操作系统死锁的必要条件(多选题):()   A.相互排斥条件   B.系统资源有限   C.进程调度不合理   D.环路等待条件 答:A,C,D 參考资料:http://blog.sina.com ...