提高性能:流的非阻塞处理

1.简介

想象一下,我们有一个需要访问外部Web服务的应用程序,以便收集有关客户端的信息,然后对其进行处理。 更具体地说,我们无法在一次调用中获得所有这些信息。 如果我们要查找不同的客户端,则需要多次调用。

如下图所示,该示例应用程序将检索有关多个客户的信息,将它们分组在一个列表中,然后对其进行处理以计算其购买总额:

流

在这篇文章中,我们将看到收集信息的不同方法,并且从性能方面来看,哪一种是最好的。

这是与Java相关的文章。 但是,我们将使用Spring框架来调用RESTful Web服务。

栏目:

  1. 介绍
  2. 解释例子
  3. 首次尝试:顺序流
  4. 提高性能:并行流
  5. 具有CompletableFuture的非阻塞处理
  6. 结论

可以在Java 8 GitHub存储库中找到源代码。

此外,您可以访问此存储库中公开RESTful Web服务的Web应用程序的源代码。

2.解释示例

在我们的应用程序中,我们有20个ID的列表,这些ID表示要从Web服务检索的客户端。 检索所有客户之后,我们将查看每个客户购买了什么,并对它们进行汇总以计算出所有客户花费的总金额是多少。

但是,有一个问题,该Web服务每次调用仅允许检索一个客户端,因此我们将需要调用该服务20次。 另外,Web服务有点慢,至少需要两秒钟才能响应请求。

如果我们看一下实现Web服务的应用程序,我们可以看到调用是由ClientController类处理的:

@RestController
@RequestMapping(value="/clients")
public class ClientController {@Autowiredprivate ClientService service;@RequestMapping(value="/{clientId}", method = RequestMethod.GET)public @ResponseBody Client getClientWithDelay(@PathVariable String clientId) throws InterruptedException {Thread.sleep(2000);Client client = service.getClient(clientId);System.out.println("Returning client " + client.getId());return client;}
}

Thread.sleep用于模拟响应速度慢。

域类(客户)包含我们需要的信息; 客户花了多少钱:

public class Client implements Serializable {private static final long serialVersionUID = -6358742378177948329L;private String id;private double purchases;public Client() {}public Client(String id, double purchases) {this.id = id;this.purchases = purchases;}//Getters and setters
}

3.首次尝试:顺序流

在第一个示例中,我们将顺序调用服务以获取所有二十个客户端的信息:

public class SequentialStreamProcessing {private final ServiceInvoker serviceInvoker;public SequentialStreamProcessing() {this.serviceInvoker = new ServiceInvoker();}public static void main(String[] args) {new SequentialStreamProcessing().start();}private void start() {List<String> ids = Arrays.asList("C01", "C02", "C03", "C04", "C05", "C06", "C07", "C08", "C09", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20");long startTime = System.nanoTime();double totalPurchases = ids.stream().map(id -> serviceInvoker.invoke(id)).collect(summingDouble(Client::getPurchases));long endTime = (System.nanoTime() - startTime) / 1_000_000;System.out.println("Sequential | Total time: " + endTime + " ms");System.out.println("Total purchases: " + totalPurchases);}
}

输出:

Sequential | Total time: 42284 ms
Total purchases: 20.0

该程序的执行大约需要42秒。 这是太多时间。 让我们看看是否可以改善其性能。

4.提高性能:并行流

Java 8允许我们将流分成多个块,并在单独的线程中处理每个流。 我们需要做的就是简单地在上一个示例中将流创建为并行流。

您应考虑到每个块将在其线程中异步执行,因此处理这些块的顺序一定无关紧要。 在我们的案例中,我们正在汇总购买量,因此我们可以做到。

让我们尝试一下:

private void start() {List<String> ids = Arrays.asList("C01", "C02", "C03", "C04", "C05", "C06", "C07", "C08", "C09", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20");long startTime = System.nanoTime();double totalPurchases = ids.parallelStream().map(id -> serviceInvoker.invoke(id)).collect(summingDouble(Client::getPurchases));long endTime = (System.nanoTime() - startTime) / 1_000_000;System.out.println("Parallel | Total time: " + endTime + " ms");System.out.println("Total purchases: " + totalPurchases);
}

输出:

Parallel | Total time: 6336 ms
Total purchases: 20.0

哇,这是一个很大的进步! 但是这个数字是什么来的呢?

并行流在内部使用ForkJoinPool,这是Java 7中引入的ForkJoin框架所使用的池。默认情况下,该池使用与计算机处理器可以处理的线程数相同的线程。 我的笔记本电脑是可以处理8个线程的四核(您可以通过调用Runtime.getRuntime.availableProcessors进行检查),因此它可以并行地对Web服务进行8次调用。 由于我们需要20次调用,因此至少需要3次“回合”:

调用

好的,所以从40秒到6秒是一个不错的改进,但是,我们还能进一步改进吗? 答案是肯定的。

5.使用CompletableFuture进行非阻塞处理

让我们分析先前的解决方案。

我们发送8个线程来调用每个Web服务,但是当该服务正在处理请求(整整两秒钟)时,我们的处理器除了等待外什么都不做(这是IO操作)。 在这些请求不回来之前,我们将无法发送更多请求。

问题是,如果我们可以异步发送所有20个请求,释放处理器并在可用时处理每个响应,该怎么办? 这是CompletableFuture抢救的地方:

public class AsyncStreamExecutorProcessing {private final ServiceInvoker serviceInvoker;private final ExecutorService executorService = Executors.newFixedThreadPool(100);public AsyncStreamExecutorProcessing() {this.serviceInvoker = new ServiceInvoker();}public static void main(String[] args) {new AsyncStreamExecutorProcessing().start();}private void start() {List<String> ids = Arrays.asList("C01", "C02", "C03", "C04", "C05", "C06", "C07", "C08", "C09", "C10", "C11", "C12", "C13", "C14", "C15", "C16", "C17", "C18", "C19", "C20");long startTime = System.nanoTime();List<CompletableFuture<Client>> futureRequests = ids.stream().map(id -> CompletableFuture.supplyAsync(() -> serviceInvoker.invoke(id), executorService)).collect(toList());double totalPurchases = futureRequests.stream().map(CompletableFuture::join).collect(summingDouble(Client::getPurchases));long endTime = (System.nanoTime() - startTime) / 1_000_000;System.out.println("Async with executor | Total time: " + endTime + " ms");System.out.println("Total purchases: " + totalPurchases);executorService.shutdown();}
}

输出:

Async with executor | Total time: 2192 ms
Total purchases: 20.0

在上一个示例中花费了三分之一的时间。

我们同时发送了所有20个请求,因此在IO操作上花费的时间仅花费了一次。 收到回复后,我们会Swift对其进行处理。

使用执行程序服务很重要,该服务设置为supplyAsync方法的可选第二个参数。 我们指定了一个包含一百个线程的池,因此我们可以同时发送100个请求。 如果我们不指定执行者,则默认情况下将使用ForkJoin池。

您可以尝试删除执行程序,您将看到与并行示例相同的性能。

六,结论

我们已经看到,当执行不涉及计算的操作(如IO操作)时,我们可以使用CompletableFuture类来利用我们的处理器并提高应用程序的性能。

翻译自: https://www.javacodegeeks.com/2015/03/improving-performance-non-blocking-processing-of-streams.html

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

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

相关文章

通过NAT转发实现私网对外发布信息

我们可以在防火墙的外部网卡上绑定多个合法IP地址&#xff0c;然后通过ip映射使发给其中某一个IP地址的包转发至内部某一用户的WWW服务器上&#xff0c;然后再将该内部WWW服务器响应包伪装成该合法IP发出的包。具体的IP分配如下&#xff1a; &#xff08;1&#xff09;该ISP分配…

java支付管理有源码_java支付宝支付案例源码

【实例简介】※运行环境※Eclipse JDK1.6及以上 Tomcat6.0及以上※使用方法※SDK下载地址&#xff1a;https://doc.open.alipay.com/docs/doc.htm?treeId193&articleId103419&docType1第一步&#xff1b;请下载【JAVA版资源】的SDK。第二步&#xff1a;下载完毕后&…

CDI和EJB:在事务成功时发送异步邮件

再一次问好&#xff01; :) 这次&#xff0c;我选择了一项常见任务&#xff0c;我认为大多数情况下都以错误的方式完成&#xff1a;发送电子邮件。 并非所有人都不知道电子邮件API的工作方式&#xff0c;例如JavaMail或Apache的commons-email 。 我通常看到的一个问题是&#…

SPFILE 、PFILE 的全面解读

这里先阐述一下数据库的启动过程&#xff1a; 1. 启动实例/例程&#xff08;nomount状态&#xff09;时&#xff0c;读取参数文件(文本文件PFILE 或服务器参数文件SPFILE)&#xff0c;分配SGA、启动后台进程、打开告警文件及后台进程跟踪文件&#xff1b; 2. 装载数据…

JAVA确定这天是这年的某一天_[Java] 练习题014: 输入某年某月某日,判断这一天是这一年的第几天?...

import java.util.*;public class Test014{public static void main(String[] args) throws Exception{int y,m,d;int sum0;int feb28;Scanner in new Scanner(System.in);System.out.print("请输入年份:");y in.nextInt();System.out.print("请输入月份:&quo…

使用默认方法的界面演变–第一部分:方法

几周前&#xff0c;我们详细研究了默认方法 -Java 8中引入的一项功能&#xff0c;该功能允许为接口方法提供实现&#xff0c;即方法主体&#xff0c;从而定义接口中的行为。 引入此功能是为了实现接口演进 。 在JDK的上下文中&#xff0c;这意味着在不破坏所有代码的情况下向接…

java左手握右手_环保型燃料—丙烷(C3H8)曾用于北京奥运会“祥云”火炬燃料,下列有关烷烃的说法不正确的是()A.丙烷分子中三个...

参考答案如下环保会祥【单选题】环境景观设计的核心就是以研究()为基础。型燃下列【判断题】构成中央处理器的两大部件是运算器和存储器。料丙料【单选题】通过下列哪种方式可以获得最强的免疫效果【单选题】根据断面图的标注形式&#xff0c;烷C烷烃下列绘制规范的断面图是【单…

windbg script ---- 禁用IsDebuggerPresent

简单的script r $t0 kernelBase!IsDebuggerPresent; eb $t00x9 31 c0 90 90强制把原代码改成xor eax, eax; nop; nop 注意在xp下&#xff0c;使用kernel32 转载于:https://www.cnblogs.com/hgy413/p/3693400.html

java两个和三个_Java语言基础(day_03)

数据类型中补充的几个小问题1)在定义Long或者Float类型变量的时候&#xff0c;要加L或者f。整数默认是int类型&#xff0c;浮点数默认晨double。byte&#xff0c;short在定义的肘候&#xff0c;他们接收的某实是一个int类型的值。这个是自己做了一个数据检测的&#xff0c;如果…

Hibernate中的JPA 2.1条件删除/更新和临时表

从JPA 2.0版开始&#xff0c; EntityManager提供了方法getCriteriaBuilder()来动态构建选择查询&#xff0c;而无需使用Java持久性查询语言&#xff08;JPQL&#xff09;进行字符串连接。 在2.1版中&#xff0c;此CriteriaBuilder提供了两个新方法createCriteriaDelete()和crea…

使用Hamcrest增强JUnit的测试能力

package com.jadyer.service;import java.util.HashMap; import java.util.Map;import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test;/*** 使用Hamcrest增强JUnit的测试能力* see Hamcrest框架提供了一些相对通俗并高效的方法来进行一些junit比较困…

在Websphere 8.0上安装Liferay 6.2 Enterprise Edition

为Liferay准备Websphere 当应用服务器二进制文件均已安装完毕&#xff0c;启动WebSphere应用服务器&#xff08;WAS&#xff09; 配置文件管理工具来创建一个配置文件适用于Liferay的和遵循的指示说明这里正式Liferay的文档。 这些说明用于在Websphere 8.5上安装Liferay 6.2&am…

java完数流程图_编程基本功训练:流程图画法及练习

对于“程序设计”的工作&#xff0c;许多初学者的理解就是“写代码”。同样&#xff0c;新手们苦恼的问题是&#xff0c;他们只会“写代码”。当接到一个新的任务&#xff0c;不少人总是在第一时间就爬到键盘上去敲代码。敲着敲着&#xff0c;就把自己绕糊涂了。头晕脑胀地坚持…

iOS modal view的关闭和显示问题

今天遇到一个问题&#xff1a; 需要关闭当前modal view&#xff0c;再present另一个modal view。 如果这么写&#xff1a; [self dismissModalViewControllerAnimated:NO];[self.delegate showBorderDetectionView];执行了第一句&#xff0c;再执行第二句的时候&#xff0c;sel…

西工大java高级网络编程_奥鹏西工大16春《JAVA高级网络编程》平时作业

西工大16春《JV高级网络编程》平时作业一、单选题(共 25 道试题&#xff0c;共 100 分。)1. 假设以tomt为w服务器&#xff0c;在hllopp应用中有一个hllo.jsp&#xff0c;它的文件路径如下: %TOMT_HOM%/wpps/hllopp/hllo/hllo.jsp 那么在浏览器端访问hllo.jsp的URL是()。. http:…

c#导出Excel

问题&#xff1a;用户查询一些数据&#xff0c;需要对页面上的数据生成excel文件。 分析&#xff1a;写文件&#xff0c;用excel进程&#xff0c;或者使用response方法&#xff08;都是通过网上查资料&#xff0c;个人就理解成这两个&#xff09; 使用excel进程有一个确定&…

使用Java 8流遍历递归数据结构

Streams API是Java 8中的真正瑰宝&#xff0c;我一直在为它们寻找或多或少的意外用途。 我最近写过有关将它们用作ForkJoinPool门面的文章 。 这是另一个有趣的例子&#xff1a;遍历递归数据结构。 事不宜迟&#xff0c;请看一下代码&#xff1a; class Tree {private int va…

mysql把用户权限授予新用户_MySQL新建普通用户和库并授予新用户对新库的所有权限...

新建库新建cmf和amon两个库&#xff0c;并指定默认字符集mysql> create database cmf DEFAULT CHARACTER SET utf8;Query OK, 1 row affected (0.00 sec)mysql> create database amon DEFAULT CHARACTER SET utf8;Query OK, 1 row affected (0.00 sec)新建用户新建cmf和a…

快速入门:OpenShift上的Spring Boot和WildfFly 8.2

与Spring Boot&#xff0c;WildFly和OpenShift相比&#xff0c;这是真正的“快速入门”&#xff0c;与我上一篇更具描述性的文章相反。 先决条件 在开始构建应用程序之前&#xff0c;我们需要安装一个OpenShift免费帐户和客户端工具。 步骤1&#xff1a;建立WildFly应用程式 …

java开发环境搭建 pdf_01搭建java web开发环境.pdf

01搭建java web开发环境.pdf还剩19页未读&#xff0c;继续阅读下载文档到电脑&#xff0c;马上远离加班熬夜&#xff01;亲&#xff0c;很抱歉&#xff0c;此页已超出免费预览范围啦&#xff01;如果喜欢就下载吧&#xff0c;价低环保&#xff01;内容要点&#xff1a;( 7)在 M…