Java中的Fork / Join框架的简要概述

Fork / Join框架是使用并发分治法解决问题的框架。 引入它们是为了补充现有的并发API。 在介绍它们之前,现有的ExecutorService实现是运行异步任务的流行选择,但是当任务同质且独立时,它们会发挥最佳作用。 运行依赖的任务并使用这些实现来组合其结果并不容易。 随着Fork / Join框架的引入,人们试图解决这一缺陷。 在本文中,我们将简要介绍API,并解决几个简单的问题以了解其工作原理。

解决非阻塞任务

让我们直接跳入代码。 让我们创建一个任务,该任务将返回List的所有元素的总和。 以下步骤以伪代码表示我们的算法:

01.查找列表的中间索引

02.在中间划分列表

03.递归创建一个新任务,该任务将计算剩余部分的总和

04.递归创建一个新任务,该任务将计算正确部分的总和

05.将左总和,中间元素和右总和的结果相加

这是代码–

@Slf4j
public class ListSummer extends RecursiveTask<Integer> {private final List<Integer> listToSum;ListSummer(List<Integer> listToSum) {this.listToSum = listToSum;}@Overrideprotected Integer compute() {if (listToSum.isEmpty()) {log.info("Found empty list, sum is 0");return 0;}int middleIndex = listToSum.size() / 2;log.info("List {}, middle Index: {}", listToSum, middleIndex);List<Integer> leftSublist = listToSum.subList(0, middleIndex);List<Integer> rightSublist = listToSum.subList(middleIndex + 1, listToSum.size());ListSummer leftSummer = new ListSummer(leftSublist);ListSummer rightSummer = new ListSummer(rightSublist);leftSummer.fork();rightSummer.fork();Integer leftSum = leftSummer.join();Integer rightSum = rightSummer.join();int total = leftSum + listToSum.get(middleIndex) + rightSum;log.info("Left sum is {}, right sum is {}, total is {}", leftSum, rightSum, total);return total;}
}

首先,我们扩展了ForkJoinTask的RecursiveTask子类型。 这是我们期望并发任务返回结果时的扩展类型。 当任务不返回结果而仅执行效果时,我们扩展RecursiveAction子类型。 对于我们解决的大多数实际任务,这两个子类型就足够了。

其次,RecursiveTask和RecursiveAction都定义了一种抽象计算方法。 这是我们进行计算的地方。

第三,在我们的计算方法内部,我们检查通过构造函数传递的列表的大小。 如果为空,则我们已经知道总和的结果为零,然后我们立即返回。 否则,我们将列表分为两个子列表,并创建ListSummer类型的两个实例。 然后,我们在这两个实例上调用fork()方法(在ForkJoinTask中定义)–

leftSummer.fork();
rightSummer.fork();

导致将这些任务安排为异步执行的原因,稍后将在本文中解释用于此目的的确切机制。

之后,我们调用join()方法(也在ForkJoinTask中定义)以等待这两部分的结果

Integer leftSum = leftSummer.join();
Integer rightSum = rightSummer.join();

然后将其与列表的中间元素相加以获得最终结果。

添加了许多日志消息,以使示例更易于理解。 但是,当我们处理包含数千个条目的列表时,拥有详细的日志记录(尤其是记录整个列表)可能不是一个好主意。

就是这样。 现在为测试运行创建一个测试类–

public class ListSummerTest {@Testpublic void shouldSumEmptyList() {ListSummer summer = new ListSummer(List.of());ForkJoinPool forkJoinPool = new ForkJoinPool();forkJoinPool.submit(summer);int result = summer.join();assertThat(result).isZero();}@Testpublic void shouldSumListWithOneElement() {ListSummer summer = new ListSummer(List.of(5));ForkJoinPool forkJoinPool = new ForkJoinPool();forkJoinPool.submit(summer);int result = summer.join();assertThat(result).isEqualTo(5);}@Testpublic void shouldSumListWithMultipleElements() {ListSummer summer = new ListSummer(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9));ForkJoinPool forkJoinPool = new ForkJoinPool();forkJoinPool.submit(summer);int result = summer.join();assertThat(result).isEqualTo(45);}
}

在测试中,我们创建一个ForkJoinPool的实例。 ForkJoinPool是用于运行ForkJoinTasks的唯一ExecutorService实现。 它采用一种称为工作窃取算法的特殊算法。 与其他ExecutorService实现相反,在该实现中,只有一个队列包含要执行的所有任务,在工作窃取实现中,每个工作线程都获得其工作队列。 每个线程都从其队列开始执行任务。

当我们检测到ForkJoinTask可以分解为多个较小的子任务时,便将它们分解为较小的任务,然后在这些任务上调用fork()方法。 该调用导致子任务被推入执行线程的队列中。 在执行期间,当一个线程用尽队列/没有要执行的任务时,它可以从其他线程的队列中“窃取”任务(因此称为“工作窃取”)。 与使用任何其他ExecutorService实现相比,这种窃取行为可以带来更高的吞吐量。

之前,当我们在leftSummer和rightSummer任务实例上调用fork()时,它们被推入执行线程的工作队列中,之后它们被池中的其他活动线程“偷”(依此类推),因为它们确实那时没有其他事情要做。

很酷吧?

解决阻止任务

我们刚才解决的问题本质上是非阻塞的。 如果我们想解决一个阻塞操作的问题,那么为了获得更好的吞吐量,我们将需要改变策略。

让我们用另一个例子来研究一下。 假设我们要创建一个非常简单的网络搜寻器。 该搜寻器将接收HTTP链接列表,执行GET请求以获取响应主体,然后计算响应长度。 这是代码–

@Slf4j
public class ResponseLengthCalculator extends RecursiveTask<Map<String, Integer>> {private final List<String> links;ResponseLengthCalculator(List<String> links) {this.links = links;}@Overrideprotected Map<String, Integer> compute() {if (links.isEmpty()) {log.info("No more links to fetch");return Collections.emptyMap();}int middle = links.size() / 2;log.info("Middle index: {}", links, middle);ResponseLengthCalculator leftPartition = new ResponseLengthCalculator(links.subList(0, middle));ResponseLengthCalculator rightPartition = new ResponseLengthCalculator(links.subList(middle + 1, links.size()));log.info("Forking left partition");leftPartition.fork();log.info("Left partition forked, now forking right partition");rightPartition.fork();log.info("Right partition forked");String middleLink = links.get(middle);HttpRequester httpRequester = new HttpRequester(middleLink);String response;try {log.info("Calling managedBlock for {}", middleLink);ForkJoinPool.managedBlock(httpRequester);response = httpRequester.response;} catch (InterruptedException ex) {log.error("Error occurred while trying to implement blocking link fetcher", ex);response = "";}Map<String, Integer> responseMap = new HashMap<>(links.size());Map<String, Integer> leftLinks = leftPartition.join();responseMap.putAll(leftLinks);responseMap.put(middleLink, response.length());Map<String, Integer> rightLinks = rightPartition.join();responseMap.putAll(rightLinks);log.info("Left map {}, middle length {}, right map {}", leftLinks, response.length(), rightLinks);return responseMap;}private static class HttpRequester implements ForkJoinPool.ManagedBlocker {private final String link;private String response;private HttpRequester(String link) {this.link = link;}@Overridepublic boolean block() {HttpGet headRequest = new HttpGet(link);CloseableHttpClient client = HttpClientBuilder.create().disableRedirectHandling().build();try {log.info("Executing blocking request for {}", link);CloseableHttpResponse response = client.execute(headRequest);log.info("HTTP request for link {} has been executed", link);this.response = EntityUtils.toString(response.getEntity());} catch (IOException e) {log.error("Error while trying to fetch response from link {}: {}", link, e.getMessage());this.response = "";}return true;}@Overridepublic boolean isReleasable() {return false;}}
}

我们创建ForkJoinPool.ManagedBlocker的实现,在其中放置阻塞的HTTP调用。 该接口定义了两个方法– block()和isReleasable() 。 block()方法是我们进行阻塞调用的地方。 在完成阻塞操作之后,我们返回true,指示不再需要进一步的阻塞。 我们从isReleasable()实现中返回false,以向fork-join工作线程指示block()方法实现本质上可能在阻塞。 isReleasable()实现将在调用block()方法之前先由fork-join工作线程调用。 最后,我们通过调用ForkJoinPool.managedBlock()静态方法将HttpRequester实例提交到池中。 之后,我们的阻止任务将开始执行。 当它阻塞HTTP请求时,如果有必要,ForkJoinPool.managedBlock()方法还将安排激活备用线程,以确保足够的并行性。

那么,让我们将此实现用于测试驱动! 这是代码–

public class ResponseLengthCalculatorTest {@Testpublic void shouldReturnEmptyMapForEmptyList() {ResponseLengthCalculator responseLengthCalculator = new ResponseLengthCalculator(Collections.emptyList());ForkJoinPool pool = new ForkJoinPool();pool.submit(responseLengthCalculator);Map<String, Integer> result = responseLengthCalculator.join();assertThat(result).isEmpty();}@Testpublic void shouldHandle200Ok() {ResponseLengthCalculator responseLengthCalculator = new ResponseLengthCalculator(List.of("http://httpstat.us/200"));ForkJoinPool pool = new ForkJoinPool();pool.submit(responseLengthCalculator);Map<String, Integer> result = responseLengthCalculator.join();assertThat(result).hasSize(1).containsKeys("http://httpstat.us/200").containsValue(0);}@Testpublic void shouldFetchResponseForDifferentResponseStatus() {ResponseLengthCalculator responseLengthCalculator = new ResponseLengthCalculator(List.of("http://httpstat.us/200","http://httpstat.us/302","http://httpstat.us/404","http://httpstat.us/502"));ForkJoinPool pool = new ForkJoinPool();pool.submit(responseLengthCalculator);Map<String, Integer> result = responseLengthCalculator.join();assertThat(result).hasSize(4);}
}

今天就这样,伙计们! 与往常一样,任何反馈/改进建议/评论都将受到高度赞赏!

此处讨论的所有示例都可以在Github上找到( 特定提交 )。

大呼大叫的http://httpstat.us服务,对于开发简单的测试非常有帮助。

翻译自: https://www.javacodegeeks.com/2019/01/brief-overview-fork-join-framework-java.html

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

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

相关文章

【渝粤题库】陕西师范大学163106旅游心理学 作业【专升本】

陕西师范大学 内 部 题 库 教育 &#xff08;yuyueshool&#xff09; 编制 陕西师范大学 内 部 题 库 教育 &#xff08;yuyueshool&#xff09; 编制 《旅游心理学》作业 一、单选题 1、旅游心理学是心理学的一个分支学科,这句话正确与否&#xff1f;&#xff08; &#x…

【渝粤题库】陕西师范大学164204 供应链与物流管理 作业

《供应链与物流管理》作业 一、单选题 1、是围绕核心企业&#xff0c;通过对信息流、物流、资金流的控制&#xff0c;从采购原材料开始&#xff0c;制成中间产品以及最终产品&#xff0c;最后由销售网络把产品送到消费者手中的将供应商、制造商、分销商、零售商、直到最终用户…

matlab 球坐标绘图,MATLAB绘制地图

1使用向量绘制地图1.1绘制全球海岸线向量数据可以表示一个地图。这种向量存在的形式是一系列的经纬度或投影坐标对&#xff0c;它们代表一个点集、一个线条或者多边形。例如&#xff0c;描绘出行政区域边界的点、公路系统、城市的中心或者以上三个集合放在一起&#xff0c;都可…

【渝粤题库】陕西师范大学165210 国际人力资源管理 作业(专升本)

一、【单项选择题】 1.以下属于道林观点的是&#xff08; &#xff09; A.更多的事物 B.更复杂的管理 C.考虑更多的人力资源因素 D.存在高瞻远瞩的考虑、较大的范围与活动等差异 2.管理人员母国化策略属于&#xff08; &#xff09; A.本国中心模式 B.多中心模式 C.全能模式 D.…

【渝粤题库】陕西师范大学200371 拓扑学 作业 (专升本、高起本)

《拓扑学》作业 单项选择 1&#xff0e;关于笛卡儿积&#xff0c;下面等式成立的是 &#xff08;A&#xff09; &#xff08;B&#xff09; &#xff08;C&#xff09; &#xff08;D&#xff09;当且仅当 2&#xff0e;设是映射&#xff0c;&#xff0c;&#xff0c;则下面结论…

php符号教程,PHP教程:网站开发编程中的特殊符号处理_php

1 php中单引号与双引号的区别在PHP中&#xff0c;通常一个字符串被定义在一对引号中&#xff0c;如&#xff1a;I am a string in single quoteshttp://www.gaodaima.com/48558.htmlPHP教程:网站开发编程中的特殊符号处理_php"I am a string in double quotes"PHP语法…

java 鲜为人知的知识点_鲜为人知的Java 8功能:广义目标类型推断

java 鲜为人知的知识点遍历Java 8的功能列表 &#xff0c; 广义目标类型推断使我震惊&#xff0c;因为它是一个特别有趣&#xff0c;鲜为人知的瑰宝。 看起来Java语言设计人员将减轻过去使用泛型&#xff08;Java 5-7&#xff09;时遇到的某些痛苦。 让我们看看他们的例子&…

【渝粤题库】陕西师范大学200701 数字逻辑

《数字逻辑》作业 一、单项选择题 1&#xff0e; 八进制数的十六进制数是 。 A. B. C. D. 2&#xff0e; 用0&#xff0c;1两个符号对100个信息进行编码&#xff0c;则至少需要 。 A. 8位 B. 7位 C. 9位 D. 6位 3&#xff0e;逻辑函数 A. B. C. D. 4&#xff0e;逻辑函数的最小…

【渝粤题库】陕西师范大学201301 《经济法学》作业(高起本、专升本)

《经济法学》作业 一、名词解释 经济法律关系主体 消费者 经济职责 行政垄断 产品责任 经济法的地位 产品质量责任、 经济法律关系主体 经济职责 行政垄断 经济法律关系的客体 经营者 政府指导价 公平交易权 瑕疵 经济法 经济法律关系 消费者权利 不正当竞争 经济法责任 二、…

【渝粤题库】陕西师范大学201721 数学教育学 作业(专升本)

《数学教育学》作业 一&#xff0e;解词 1.认知结构&#xff1a;2.联结说 3.同化与顺应 4.非认知因素 5. 数学素质 二&#xff0e;填空&#xff1a; 1&#xff0e;数学教育学是研究 的一门专业化学科。 2&#xff0e;中学数学课程的改革势在必行&#xff0c;至少有以下几点原因…

探索适用于Apache Spark的Spline Data Tracker和可视化工具(第2部分)

在第1部分中&#xff0c;我们学习了如何使用以下方法测试数据沿袭信息收集 Spark外壳中的花键 。 在任何Scala或Java Spark应用程序中都可以这样做。 需要在您选择的构建工具&#xff08;Maven&#xff0c;Gradle或sbt&#xff09;中注册与Spark Shell相同的依赖项&#xff1a…

matlab 着色算法,colorization_matlab着色 - 源码下载|图形图象|图形图像处理(光照,映射..)|源代码 - 源码中国...

colorization_matlab着色colorization_matlab着色\READMEcolorization_matlab着色\README~colorization_matlab着色\cheapUI.mcolorization_matlab着色\colorize.mcolorization_matlab着色\colorizeFun.mcolorization_matlab着色\defs.hcolorization_matlab着色\example.bmpcol…

【渝粤题库】陕西师范大学202091公共管理学原理 作业(高起本、专升本)

《公共管理学原理》作业 一、名词解释 1&#xff0e;治理    &#xff12;&#xff0e;公共物品    &#xff13;&#xff0e;公共政策  4&#xff0e;公共部门人力资源开发 5&#xff0e;管理幅度 6 &#xff0e; 组织   7 &#xff0e;绩效管理   8 &#xff0e; 第三…

【渝粤题库】陕西师范大学202871 婚姻家庭法作业

《婚姻家庭法》作业 一、名词解释 1&#xff0e;婚姻 2&#xff0e;亲属 3&#xff0e;探望权 4&#xff0e;重婚 5&#xff0e;亲权 6&#xff0e;血亲7&#xff0e;行政离婚 8&#xff0e;事实婚姻 9&#xff0e;婚生子女 10&#xff0e;收养 二、填空题 1&#xff0e;家庭是…

matlab中inf函数,matlab中voronoin()函数的用法,求高手指点

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼x [ 0.5 0;0 0.5;-0.5 -0.5;-0.2 -0.1;-0.1 0.1;0.1 -0.1;0.1 0.1 ];voronoi(x(:,1),x(:,2))[v,c]voronoin(x)for i1:length(c)disp(c{i})end输出结果如下&#xff1a;v Inf Inf0.7000 -1.6500-0.0500 -0.0500-0.0500 -0.5250-1.4…

java编写正则表达式_如何用Java编写最快的表达式评估器之一

java编写正则表达式当然&#xff0c;标题有点吸引人&#xff0c;但确实如此&#xff08;您当然不相信自己没有伪造自己的基准&#xff0c;但这是另一回事了&#xff09;。 因此&#xff0c;上周我正在寻找一个小型且可用的库来评估数学表达式。 我几乎直接偶然发现了这个stack…

【渝粤题库】陕西师范大学210029 幼儿园游戏(学前儿童游戏)作业

《幼儿园游戏》作业 一、单选题 1、我国传统的游戏材料“七巧板”在国外称为&#xff08; &#xff09;。 2、原始人的游戏形式是高度融合的&#xff0c;主要表现为想象性游戏和&#xff08; &#xff09;两种游戏。 3、教育对幼儿具有&#xff08; &#xff09;的价值。 4、亲…

【渝粤题库】陕西师范大学292391 金融机构管理 作业(专升本)

一、单项选择题 1、“经理国库、充当最后贷款人”表示中央银行在行使( )职能。 A&#xff0e;调节职能 B&#xff0e;服务职能 C&#xff0e;管理职能 D&#xff0e;控制职能 2、我国国民经济核算新体系由( )组成。 A&#xff0e;资产负债表和资金流量表 B&#xff0e;国际收支…

matlab图像采集程序,用摄像头连续采集、保存图像源程序

写了一个在matlab2006上用摄像头连续采集、保存图像源程序。运行imaq_test.m文件后&#xff0c;弹出保存对话框&#xff0c;指定一个存盘目录&#xff0c;选定保存格式(如.jpg)&#xff0c;输入主文件名(如aqim)&#xff0c;点击开始采集按钮&#xff0c;将以设定的频率采集图像…

【渝粤题库】陕西师范大学400010 当代西方社会思潮评析 作业(专升本)

《当代西方社会思潮评析》作业 一、谈谈你对下列概念的理解 1、第三条道路 2、生态社会主义 3、后殖民主义 4、未来主义 5、新自由主义 6、新自由主义 7、后现代主义 8、女权社会主义 9、市场社会主义 10、分析的马克思主义 二、简要回答下列各题 1、“兰格模式”的主要内容与理…