Java Fork / Join进行并行编程

最近几年,计算机处理器领域发生了范式转变。 多年来,处理器制造商一直在提高时钟频率,因此开发人员享受到这样的事实,即他们的单线程软件执行得更快,而无需他们付出任何努力。 现在,处理器制造商青睐多核芯片设计,并且必须以多线程或多进程的方式编写软件才能充分利用硬件。 因此,软件开发人员追赶的唯一方法是编写利用并行性的应用程序,即使用多个CPU内核来处理所有任务,而不是使用单个更快的内核。 摩尔定律仍然适用,但适用范围有所不同。

并行计算或并行化是一种计算形式,其中许多计算同时进行,其原理是大问题通常可以分为较小的问题,然后并行解决(“并行”)。 从本质上讲,如果可以将CPU密集型问题划分为较小的独立任务,则可以将这些任务分配给不同的处理器。

关于多线程和并发,Java非常有趣。 它从一开始就支持Thread,而在过去,您可以使用带有interrupt , join , sleep方法的低级方法来操纵线程的执行。 此外,所有对象继承的notify和wait方法也可能会有所帮助。

可以通过这种方式控制应用程序的执行,但是过程有些繁琐。 然后是Java 1.5中的并发包 ,它提供了一个更高级别的框架,开发人员可以使用该框架以更简单,更容易和更不易出错的方式处理线程。 该程序包提供了一堆在并发编程中通常有用的实用程序类。

多年以来,对“启用并发”程序的需求变得越来越大,因此该平台采取了这一步骤,进一步引入了Java SE 7的新并发功能 。 功能之一是引入了Fork / Join框架,该框架原本打算包含在JDK 1.5版本中,但最终没有成功。

Fork / Join框架旨在使分而治之算法易于并行化。 这种类型的算法非常适合可以分为两个或多个相同类型的子问题的问题。 他们使用递归将问题分解为简单的任务,直到这些任务变得足够简单以至于可以直接解决。 然后将子问题的解决方案合并以给出原始问题的解决方案。 关于Fork / Join方法的出色介绍是文章“ Java SE中的Fork-Join开发 ”。

您可能已经注意到,Fork / Join方法类似于MapReduce ,因为它们都是并行化任务的算法。 但是,一个区别是,只有在必要时(如果太大),Fork / Join任务才将自己细分为较小的任务,而MapReduce算法将其作为工作的第一步将所有工作分为几部分。

Fork / Join Java框架起源于JSR-166,您可以在Doug Lea领导的并发JSR-166兴趣站点中找到更多信息。 实际上,如果您希望使用该框架并且没有JDK 7,则必须在这里进行操作。如站点中所述,我们可以使用JDK 1.5和1.6中的相关类,而不必安装最新的JDK。 为此,我们必须下载jsr166 jar并使用-Xbootclasspath / p:jsr166.jar选项启动JVM。 请注意,您可能需要在“ jsr166.jar”之前加上其完整文件路径。 之所以必须这样做,是因为JAR包含一些重写核心Java类的类(例如java.util包下的类)。 不要忘记为JSR-166 API文档添加书签。

因此,让我们看看如何使用该框架解决一个实际问题。 我们将创建一个程序来计算斐波纳契数 (一个经典的教学问题),并查看如何使用新类使我们尽可能快。 Java Fork / Join + Groovy文章中也解决了此问题。

首先,我们创建一个表示问题的类,如下所示:

package com.javacodegeeks.concurrency.forkjoin;public class FibonacciProblem {public int n;public FibonacciProblem(int n) {this.n = n;}public long solve() {return fibonacci(n);}private long fibonacci(int n) {System.out.println("Thread: " +Thread.currentThread().getName() + " calculates " + n);if (n <= 1)return n;else return fibonacci(n-1) + fibonacci(n-2);}}

如您所见,我们使用该解决方案的递归版本,这是一个典型的实现。 (请注意,此实现效率很高,因为它会一遍又一遍地计算相同的值。在实际情况下,已经缓存的计算值应在以后的执行中进行缓存和检索)。 现在让我们看一下单线程方法的外观:

package com.javacodegeeks.concurrency.forkjoin;import org.perf4j.StopWatch;public class SillyWorker {public static void main(String[] args) throws Exception {int n = 10;StopWatch stopWatch = new StopWatch();FibonacciProblem bigProblem = new FibonacciProblem(n); long result = bigProblem.solve();   stopWatch.stop();System.out.println("Computing Fib number: " + n);System.out.println("Computed Result: " + result);System.out.println("Elapsed Time: " + stopWatch.getElapsedTime());}}

我们只是创建一个新的FibonacciProblem并执行其solve方法,该方法将递归调用fibonacci方法。 我还使用漂亮的Perf4J库来跟踪经过的时间。 输出将是这样的(我隔离了最后几行): …线程:main计算1线程:main计算0计算Fib数:10计算结果:55经过的时间:8如预期的那样,所有工作都已完成只有一个线程(主线程)。 让我们看看如何使用Fork / Join框架重写它。 请注意,在Fibonacci解决方案中,将发生以下情况: fibonacci(n-1)+ fibonacci(n-2)因此,我们可以将这两个任务中的每一个分配给一个新的工作程序(即新线程),然后工人已经完成处决,我们将加入结果。 考虑到这一点,我们引入了FibonacciTask类,该类本质上是将较大的Fibonacci问题划分为较小的问题的一种方法。

package com.javacodegeeks.concurrency.forkjoin;import java.util.concurrent.RecursiveTask;public class FibonacciTask extends RecursiveTask<Long> {private static final long serialVersionUID = 6136927121059165206L;private static final int THRESHOLD = 5;private FibonacciProblem problem;public long result;public FibonacciTask(FibonacciProblem problem) {this.problem = problem;}@Overridepublic Long compute() {if (problem.n < THRESHOLD) { // easy problem, don't bother with parallelismresult = problem.solve();}else {FibonacciTask worker1 = new FibonacciTask(new FibonacciProblem(problem.n-1));FibonacciTask worker2 = new FibonacciTask(new FibonacciProblem(problem.n-2));worker1.fork();result = worker2.compute() + worker1.join();}return result;}}

注意:如果您没有使用JDK 7,而是手动包含了JSR-166库,则必须覆盖默认的Java核心类。 否则,您将遇到以下错误: java.lang.SecurityException:禁止的程序包名称:java.util.concurrent为避免这种情况,请使用以下参数将JVM设置为覆盖类: -Xbootclasspath / p:lib / jsr166.jar我使用了“ lib / jsr166.jar”值,因为该JAR驻留在我的Eclipse项目中一个名为“ lib”的文件夹中。 配置如下所示:

我们的任务扩展了RecursiveTask类,该类是递归结果的ForkJoinTask 。 我们将覆盖处理此任务执行的主要计算的计算方法。 在该方法中,我们首先检查是否必须使用并行性(通过与阈值进行比较)。 如果这是一个易于执行的任务,我们将直接调用solve方法,否则我们将创建两个较小的任务,然后分别执行每个任务。 执行发生在不同的线程中,然后将其结果合并。 这可以通过使用fork和join方法来实现。 让我们测试一下实现:

package com.javacodegeeks.concurrency.forkjoin;import java.util.concurrent.ForkJoinPool;import org.perf4j.StopWatch;public class ForkJoinWorker {public static void main(String[] args) {// Check the number of available processorsint processors = Runtime.getRuntime().availableProcessors();System.out.println("No of processors: " + processors);int n = 10;StopWatch stopWatch = new StopWatch();   FibonacciProblem bigProblem = new FibonacciProblem(n);FibonacciTask task = new FibonacciTask(bigProblem);ForkJoinPool pool = new ForkJoinPool(processors);pool.invoke(task);long result = task.result;System.out.println("Computed Result: " + result);stopWatch.stop();System.out.println("Elapsed Time: " + stopWatch.getElapsedTime());}}

我们首先检查系统中可用的处理器数量,然后创建具有相应并行度的新ForkJoinPool 。 我们使用invoke方法分配并执行任务。 这是输出(我已经隔离了第一个和最后一个方法): 处理器数量:8线程:ForkJoinPool-1-worker-7计算4线程:ForkJoinPool-1-worker-6计算4线程:ForkJoinPool-1-worker- 4计算4…线程:ForkJoinPool-1-worker-2计算1线程:ForkJoinPool-1-worker-2计算0计算结果:55经过的时间:16请注意,现在将计算委托给多个工作线程,每个工作线程其中的一项任务比原来的任务要小。 您可能已经注意到,经过的时间大于上一个。 之所以发生这种矛盾,是因为我们使用了较低的阈值(5)和较低的n值(10)。 由于创建了大量线程,因此引入了不必要的延迟。 对于较大的阈值(大约20)和较高的n(40和更高),框架的真正力量将变得显而易见。 我对n> 40的值进行了一些快速压力测试,下面是带有结果的图表:

显然,Fork / Join框架的伸缩性比单线程方法好得多,并且可以在更短的时间内执行计算。 (如果您希望自己进行一些压力测试,请不要忘记删除FibonacciProblem类中的System.out调用。)查看Windows 7计算机( i7- 720QM(具有4个内核和超线程 ),同时使用了每种方法。


单线程: 在执行过程中,CPU的总使用率仍然很低(从未超过16%)。 如您所见,当单线程应用程序难以执行所有计算时,CPU利用率不足。

多线程:

CPU利用率要好得多,所有处理器都有助于进行总计算。 在Java中介绍Fork / Join框架时,我们已经结束了。 请注意,这里我只是做些表面介绍,还存在许多其他功能,它们随时可以帮助我们利用多核CPU。 一个新的时代正在出现,因此开发人员应该熟悉这些概念。 与往常一样,您可以在这里找到为本文生成的源代码。 别忘了分享!

相关文章 :
  • 正确记录应用程序的10个技巧
  • 每个程序员都应该知道的事情
  • Java最佳实践–多线程环境中的DateFormat
  • Java最佳实践–队列之战和链接的ConcurrentHashMap
  • Java最佳实践–高性能序列化
相关片段:
  • 更一般的等待/通知机制的CountDownLatch示例
  • 受限连接池的阻塞队列示例
  • 任务运行器的重入锁示例
  • 可重入ReadWriteLock值计算器示例

翻译自: https://www.javacodegeeks.com/2011/02/java-forkjoin-parallel-programming.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/374757.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

arm-elf-gcc交叉编译器的使用教程

arm-elf-gcc交叉编译器的使用教程 一开始需要安装arm-elf-gcc&#xff0c;但是这是一个32位的程序&#xff0c;我是安装了64位的系统&#xff0c;据说安装ia32.libs依赖库能运行这个&#xff0c;但是看到博客上面前人安装完了系统图标少了一半&#xff0c;然后就怕了。经过了翻…

力扣删除排序数组中的重复项 II

给你一个有序数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 最多出现两次 &#xff0c;返回删除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 思路&#xff1a; 双指针…

2 android学习资料

http://blog.csdn.net/lmj623565791 http://blog.csdn.net/harvic880925/article/details/50995268转载于:https://www.cnblogs.com/YyuTtian/p/5440930.html

建立自己的GWT Spring Maven原型

大家好&#xff0c; 在观看Justin撰写的有关Spring和GWT的非常有趣的文章时&#xff0c;我认为展示如何构建自己的自定义Maven原型非常有用。我们将展示的原型基于Justin的上一个项目&#xff0c;并包括各种技术&#xff0c;例如Spring &#xff0c; GWT &#xff0c; AspectJ…

C# 连接Oracle数据库异常总结

这2天因为工作需要连接Oracle数据库&#xff0c;中间发生了很多问题 一、使用OleDbConnection连接数据库 ------------------ ProviderOraOLEDB.Oracle.1;User IDsajet;Passwordtech;Data Source(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST 192.168.66.225)(…

力扣颜色分类

给定一个包含红色、白色和蓝色&#xff0c;一共 n 个元素的数组&#xff0c;原地对它们进行排序&#xff0c;使得相同颜色的元素相邻&#xff0c;并按照红色、白色、蓝色顺序排列。 此题中&#xff0c;我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 思路:将红色和蓝色…

Cassandra,MongoDB,CouchDB,Redis,Riak,HBase比较

克里斯托夫科瓦奇&#xff08;KristfKovcs&#xff09;对六个最受欢迎的“ NoSQL ”数据库实现进行了非常有趣的简短比较 。 除了Kristf的工作之外&#xff0c;我还想提供一些链接&#xff0c;我相信这些链接将对有兴趣关注“ NoSQL ”社区的所有人员提供帮助&#xff1a; No…

程序员需要谨记的九大安全编码规则

历史已经证明&#xff0c;软件设计的缺陷一直是导致其漏洞被利用的最主要的罪魁祸首。安全专家发现&#xff0c;多数漏洞源自常见软件中相对有限的一些漏洞。软件开发者和设计者应当严格检查程序中的各种错误&#xff0c;尽量在软件部署之前就减少或清除其中的漏洞。 下面列举的…

HDU 2897

Problem Description当日遇到月&#xff0c;于是有了明。当我遇到了你&#xff0c;便成了侣。那天&#xff0c;日月相会&#xff0c;我见到了你。而且&#xff0c;大地失去了光辉&#xff0c;你我是否成侣&#xff1f;这注定是个凄美的故事。&#xff08;以上是废话&#xff09…

力扣合并两个有序数组

题目&#xff1a;给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#xff0c;使合并后的数组同样按 非递减顺序 排列。 注意&#xff1a;最终&#…

Google Guava库必需品

我希望代码简单&#xff0c;短而又易于阅读。 不必要的复杂性分散了人们对真实情况的理解&#xff0c;使他们难以理解&#xff0c;并且可能成为生产力的真正杀手。 您知道&#xff0c;缠结的for循环和索引可以跟踪是否/其他情况和切换用例&#xff0c;空/验证检查&#xff0c;转…

单调栈3_水到极致的题 HDOJ4252

A Famous City 题目大意 给出正视图 每一列为楼的高度 最少有几座楼 坑点 楼高度可以为0 代表没有楼 贡献了两发RE 原因 if(!s.empty()&&tem){s.push(tem); continue;}并不能筛去 空栈且 tem为0的情况 改为 if(!s.empty()){if(tem) s.push(tem); continue;} 后AC 题目…

eclipse配置远程调试

一、配置 1、cd apache-tomcat/bin 2、vi startup.sh文件 3、在文件开头处&#xff0c;添加下方代码(address代表的是调试端口) declare -x CATALINA_OPTS"-Xdebug -Xnoagent -Djava.compilerNONE -Xrunjdwp:servery,transportdt_socket,suspendn,address…

力扣两数之和 II - 输入有序数组

题目:给定一个已按照 非递减顺序排列 的整数数组 numbers &#xff0c;请你从数组中找出两个数满足相加之和等于目标数 target 。 我的代码&#xff1a; 对撞指针 class Solution {public int[] twoSum(int[] numbers, int target) {int low 0;//指向头int high numbers.le…

SpringMVC 3 Tiles 2.2.2集成教程

Apache Tiles是基于Java的Web应用程序的流行且最常用的模板框架。 由于Struts 1.x使用Tiles作为其默认模板框架&#xff0c;因此Tiles变得更加流行。 SpringMVC是一个MVC框架&#xff0c;例如Struts &#xff0c;也支持将Tiles集成为其模板框架。 让我们看看如何集成SpringMVC和…

[团队项目3.0]Scrum团队成立

Scrum团队成立 5.Scrum团队成立 5.1 团队名称&#xff0c;团队目标、团队口号、团队照&#xff1b; 5.2 角色分配 产品负责人: 决定开发内容和优先级排序&#xff0c;最大化产品以及开发团队工作的价值。 Scrum Master&#xff1a; 负责确保团队遵循 Scrum 的理论、实践和规则。…

Base64编码的java实现

Java本身是提供了Base64编码的工具包的&#xff0c;做项目的时候自己实现了个&#xff0c;在这里记录一下&#xff1a; 1 /** Base64编码数组 */2 private static final String base64EncodeChars "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456…

Character.isLetterOrDigit(ch)判断ch是否为字母或数字

Character.isLetter(ch) 判断ch是否为字母 Character.isDigit(ch) 判断ch是否为数字 Character.isLetterOrDigit(ch) 判断ch是否为字母或数字 /* 例子*/char ch q;System.out.println(Character.isLetter(ch));System.out.println(Character.isDigit(ch));System.out.print…

高级SmartGWT教程,第1部分

贾斯汀&#xff08;Justin&#xff09;&#xff0c;帕特&#xff08;Pat&#xff09;和我已经开始着手一个需要用户界面进行管理和管理的副项目。 在与SmartGWT和GWT共同工作了一段时间之后&#xff0c;我们决定使用SmartGWT创建接口。 我们非常喜欢视觉组件&#xff08;请查看…

git 技巧

将某个文件回退到某个版本 git co d359624286d9c1f022b8b3b6f2d3fe3b6524188b build.sh 查看某个文件在某个版本时的内容 git show d359624286d9c1f022b8b3b6f2d3fe3b6524188b:build.sh 如果想把这个文件重命名保存 git show d359624286d9c1f022b8b3b6f2d3fe3b6524188b:build.s…