轻松实现多核时代的并行计算

介绍

随着多核芯片逐渐成为主流,大多数软件开发人员不可避免地需要了解并行编程的知识。而同时,主流程序语言正在将越来越多的并行特性合并到标准库或者语言本身之中。我们可以看到,JDK 在这方面同样走在潮流的前方。在 JDK 标准版 5 中,由 Doug Lea 提供的并行框架成为了标准库的一部分(JSR-166)。随后,在 JDK 6 中,一些新的并行特性,例如并行 collection 框架,合并到了标准库中(JSR-166x)。直到今天,尽管 Java SE 7 还没有正式发布,一些并行相关的新特性已经出现在 JSR-166y 中:

  1. Fork/Join 模式;
  2. TransferQueue,它继承自 BlockingQueue 并能在队列满时阻塞“生产者”;
  3. ArrayTasks/ListTasks,用于并行执行某些数组/列表相关任务的类;
  4. IntTasks/LongTasks/DoubleTasks,用于并行处理数字类型数组的工具类,提供了排序、查找、求和、求最小值、求最大值等功能;

其中,对 Fork/Join 模式的支持可能是对开发并行软件来说最通用的新特性。在 JSR-166y 中,Doug Lea 实现 ArrayTasks/ListTasks/IntTasks/LongTasks/DoubleTasks 时就大量的用到了 Fork/Join 模式。读者还需要注意一点,因为 JDK 7 还没有正式发布,因此本文涉及到的功能和发布版本有可能不一样。

Fork/Join 模式有自己的适用范围。如果一个应用能被分解成多个子任务,并且组合多个子任务的结果就能够获得最终的答案,那么这个应用就适合用 Fork/Join 模式来解决。图 1 给出了一个 Fork/Join 模式的示意图,位于图上部的 Task 依赖于位于其下的 Task 的执行,只有当所有的子任务都完成之后,调用者才能获得 Task 0 的返回结果。

图 1. Fork/Join 模式示意图

可以说,Fork/Join 模式能够解决很多种类的并行问题。通过使用 Doug Lea 提供的 Fork/Join 框架,软件开发人员只需要关注任务的划分和中间结果的组合就能充分利用并行平台的优良性能。其他和并行相关的诸多难于处理的问题,例如负载平衡、同步等,都可以由框架采用统一的方式解决。这样,我们就能够轻松地获得并行的好处而避免了并行编程的困难且容易出错的缺点。

使用 Fork/Join 模式

在开始尝试 Fork/Join 模式之前,我们需要从 Doug Lea 主持的 Concurrency JSR-166 Interest Site 上下载 JSR-166y 的源代码,并且我们还需要安装最新版本的 JDK 6(下载网址请参阅 参考资源)。Fork/Join 模式的使用方式非常直观。首先,我们需要编写一个 ForkJoinTask 来完成子任务的分割、中间结果的合并等工作。随后,我们将这个 ForkJoinTask 交给 ForkJoinPool 来完成应用的执行。

通常我们并不直接继承 ForkJoinTask,它包含了太多的抽象方法。针对特定的问题,我们可以选择 ForkJoinTask 的不同子类来完成任务。RecursiveAction 是 ForkJoinTask 的一个子类,它代表了一类最简单的 ForkJoinTask:不需要返回值,当子任务都执行完毕之后,不需要进行中间结果的组合。如果我们从 RecursiveAction 开始继承,那么我们只需要重载 protected void compute() 方法。下面,我们来看看怎么为快速排序算法建立一个 ForkJoinTask 的子类:

清单 1. ForkJoinTask 的子类

class SortTask extends RecursiveAction { final long[] array; final int lo; final int hi; private int THRESHOLD = 30;  public SortTask(long[] array) { this.array = array; this.lo = 0; this.hi = array.length - 1; }  public SortTask(long[] array, int lo, int hi) { this.array = array; this.lo = lo; this.hi = hi; }  protected void compute() { if (hi - lo < THRESHOLD) sequentiallySort(array, lo, hi); else { int pivot = partition(array, lo, hi); coInvoke(new SortTask(array, lo, pivot - 1), new SortTask(array, pivot + 1, hi)); } }  private int partition(long[] array, int lo, int hi) { long x = array[hi]; int i = lo - 1; for (int j = lo; j < hi; j++) { if (array[j] <= x) { i++; swap(array, i, j); } } swap(array, i + 1, hi); return i + 1; }  private void swap(long[] array, int i, int j) { if (i != j) { long temp = array[i]; array[i] = array[j]; array[j] = temp; } }  private void sequentiallySort(long[] array, int lo, int hi) { Arrays.sort(array, lo, hi + 1); }}

在 清单 1 中,SortTask 首先通过 partition() 方法将数组分成两个部分。随后,两个子任务将被生成并分别排序数组的两个部分。当子任务足够小时,再将其分割为更小的任务反而引起性能的降低。因此,这里我们使用一个 THRESHOLD,限定在子任务规模较小时,使用直接排序,而不是再将其分割成为更小的任务。其中,我们用到了 RecursiveAction 提供的方法 coInvoke()。它表示:启动所有的任务,并在所有任务都正常结束后返回。如果其中一个任务出现异常,则其它所有的任务都取消。coInvoke() 的参数还可以是任务的数组。

现在剩下的工作就是将 SortTask 提交到 ForkJoinPool 了。ForkJoinPool() 默认建立具有与 CPU 可使用线程数相等线程个数的线程池。我们在一个 JUnit 的 test 方法中将 SortTask 提交给一个新建的 ForkJoinPool:

清单 2. 新建的 ForkJoinPool

@Testpublic void testSort() throws Exception { ForkJoinTask sort = new SortTask(array); ForkJoinPool fjpool = new ForkJoinPool(); fjpool.submit(sort); fjpool.shutdown();  fjpool.awaitTermination(30, TimeUnit.SECONDS);  assertTrue(checkSorted(array));}

在上面的代码中,我们用到了 ForkJoinPool 提供的如下函数:

  1. submit():将 ForkJoinTask 类的对象提交给 ForkJoinPool,ForkJoinPool 将立刻开始执行 ForkJoinTask。
  2. shutdown():执行此方法之后,ForkJoinPool 不再接受新的任务,但是已经提交的任务可以继续执行。如果希望立刻停止所有的任务,可以尝试 shutdownNow() 方法。
  3. awaitTermination():阻塞当前线程直到 ForkJoinPool 中所有的任务都执行结束。

并行快速排序的完整代码如下所示:

清单 3. 并行快速排序的完整代码

package tests; import static org.junit.Assert.*; import java.util.Arrays;import java.util.Random;import java.util.concurrent.TimeUnit; import jsr166y.forkjoin.ForkJoinPool;import jsr166y.forkjoin.ForkJoinTask;import jsr166y.forkjoin.RecursiveAction; import org.junit.Before;import org.junit.Test; class SortTask extends RecursiveAction { final long[] array; final int lo; final int hi; private int THRESHOLD = 0; //For demo only  public SortTask(long[] array) { this.array = array; this.lo = 0; this.hi = array.length - 1; }  public SortTask(long[] array, int lo, int hi) { this.array = array; this.lo = lo; this.hi = hi; }  protected void compute() { if (hi - lo < THRESHOLD) sequentiallySort(array, lo, hi); else { int pivot = partition(array, lo, hi); System.out.println("pivot = " + pivot + 

odoo pivot中去掉求和_JDK 7 中的 Fork/Join 模式相关推荐

  1. Java中J.U.C扩展组件之Fork,join

    Fork/join介绍 Fork/join框架是java7提供的并行执行任务的框架,是把大任务分割成若干小任务,最后汇总若干小任务的执行结果得到最终的结果.它的思想与MapReduce类似.Fork把 ...

  2. odoo pivot中去掉求和_一文读懂深度学习中的卷积运算与图像处理

    华为人工智能认证讲师 袁梦 在人工智能深度学习技术中,有一个很重要的概念就是卷积神经网络 CNN(Convolutional Neural Networks).卷积神经网络被广泛地运用到计算机视觉中, ...

  3. odoo pivot中去掉求和_评比算分,去掉最高分和最低分算平均,PLC怎样编程实现?...

    在很多比赛中为了体现公平与公正,避免个别明显过高或过低的评分影响选手的成绩,一般都会设置诸如"去掉1个最低分和1个最高分"这样的规则,那么这个算法PLC编程怎样来实现呢? 原理很简 ...

  4. vue中去掉v-for遍历数组中的最后一个逗号

    <span v-if="i<fwData.householdRegisterList.length-1">,</span> 加一个v-if判断就好了, ...

  5. 【Java面试题】54 去掉一个Vector集合中重复的元素

    在Java中去掉一个 Vector 集合中重复的元素 1)通过Vector.contains()方法判断是否包含该元素,如果没有包含就添加到新的集合当中,适用于数据较小的情况下. import jav ...

  6. 中去掉外键_【Java笔记】035天,MySQL中的增删改查

    学习Java的第35天. 今天天除了学习MySQL中的各种约束,MySQL中DML的操作,还有MySQL查询语句ds-- MySQLdz中d的约束有: • 非空约束(not null) • 唯一性约束 ...

  7. 转 如何在IOS设备中去掉屏幕上的status bar

    引入 如何在IOS设备中去掉屏幕上的status bar,即:不显示设备上方的[网络.时间.电池??]条? 操作 方法一: 在-info.list项目文件中,加上"Status bar is ...

  8. python中求和公式是什么函数_Python的math库中,用于求和的函数是( )。

    [单选题]确定兴利库容 V 兴 ,已知某水库为一回运用水库,其一次蓄水量为 V 1 =300 万 m 3 ,一次供水量为 V 2 =150 万 m 3 . [ ]. [单选题]hAB大于0说明B点的高 ...

  9. 一个整形数组中最大值求和问题(3)

    新要求: 在"一个整形数组中最大值求和问题(2)"的基础之上,加入了新要求. 将一位数组变成环形数组. 首先在自己看到这道题目的时候,并没有什么思路,然后老师一点一点的旁敲侧击,自 ...

最新文章

  1. 190401装饰器-高阶函数-闭包
  2. shell编程 字符串处理
  3. 划时代的项目管理核心引擎——DynamicGantt 动态图甘特图
  4. 77. Leetcode 1439. 有序矩阵中的第 k 个最小数组和 (堆-技巧二-多路归并)
  5. c语言dfs算法全排列代码,c语言dfs解决全排列问题
  6. 曾经采集朋友圈难倒多少Python大佬,今天手把手教你如何实现!
  7. 基于WF设计业务流程平台_权限在流程模板外部映射
  8. 非文学翻译理论与实践_2019年北京语言大学翻译学专业考研经验分享
  9. Python初学者的资源总结
  10. 李彦宏告诫年轻人:向前看两年
  11. 如何辨别真假柯达胶卷
  12. 深入.NET编程 第十一章 影院售票系统
  13. java .jar怎么打开_jar文件怎么打开,教您如何打开jar文件
  14. 今晚7:00 | PhD Debate 自监督学习在推荐系统中的应用
  15. 拼多多的商业模式和营销套路
  16. 两种依赖注入的类型是什么?
  17. 试玩广告及SmartAd平台介绍
  18. Win10安装Ubuntu18.04
  19. 英语学习——学英语的心态(2)
  20. 【网络】SNAT和DNAT

热门文章

  1. 机器人卡纸形状图片大全图片_天水生态木天花吊顶图片大全
  2. editor.md使用php,Editor.md提示修改
  3. JAVA中几种循环结构的表示_本文通过实例讲解给大家介绍Java中for、while、do while三种循环语句的区别,具体详情如下所示:第一种:for循环 循环结构for语句的格式...
  4. HALCON 21.11:深度学习笔记(2)
  5. Python+OpenCV:图像快速角点检测算法(FAST Algorithm for Corner Detection)
  6. Mysql原理+ 多实例 +表损坏
  7. HyperLeger Fabric开发(七)——HyperLeger Fabric链码开发
  8. Java父类与子类中静态代码块 实例代码块 静态变量 实例变量 构造函数执行顺序...
  9. KeyMob手机聚合平台已集成多家移动广告平台
  10. css用边框实现圆角矩形