用CountDownLatch提升请求处理速度
countdownlatch是java多线程包concurrent里的一个常见工具类,通过使用它可以借助线程能力极大提升处理响应速度,且实现方式非常优雅。今天我们用一个实际案例和大家来讲解一下如何使用以及需要特别注意的点。
由于线程类的东西都比较抽象,我们换一种讲解思路,先讲解决问题的案例,然后再解释下原理。
假设在微服务架构中,A服务会调用B服务处理一些事情,且每处理一次业务,A可能要调用B多次处理逻辑相同但数据不同的事情。为了提升整个链路的处理速度,我们自然会想到是否可以把A调用B的各个请求组成一个批次,这样A服务只需要调用B服务一次,等B服务处理完一起返回即可,省了多次网络传输的时间。代码如下:
/**
* 批次请求处理服务
* @param batchRequests 批次请求对象列表
* @return
*/
public List<DealResult> deal(List<DealRequest> batchRequests){
List<DealResult> resultList = new ArrayList<>();
if(batchRequests != null){
for(DealRequest request : batchRequests){
//遍历顺序处理单个请求
resultList.add(process(request));
}
}
return resultList;
}
但是B服务顺序处理批次里每一个请求的时间并没有节省,假设批次里有3个请求,一个请求平均耗时100MS,则B服务还是要花费300MS来处理完。有什么办法能立刻简单提升3倍处理速度,令总花费时间只需要100MS?到我们的大将countdownlatch出场了!代码如下:
/**
* 使用countdownlatch的批次请求处理服务
* @param batchRequests 批次请求对象列表
* @return
*/
public List<DealResult> countDownDeal(List<DealRequest> batchRequests){ //定义线程安全的处理结果列表
List<DealResult> countDownResultList = Collections.synchronizedList(new ArrayList<DealResult>()); if(batchRequests != null){ //定义countdownlatch线程数,有多少个请求,我们就定义多少个
CountDownLatch runningThreadNum = new CountDownLatch(batchRequests.size()); for(DealRequest request : batchRequests){
//循环遍历请求,并实例化线程(构造函数传入CountDownLatch类型的runningThreadNum),立刻启动
DealWorker dealWorker = new DealWorker(request, runningThreadNum, countDownResultList);
new Thread(dealWorker).start();
} try {
//调用CountDownLatch的await方法则当前主线程会等待,直到CountDownLatch类型的runningThreadNum清0
//每个DealWorker处理完成会对runningThreadNum减1
//如果等待1分钟后当前主线程都等不到runningThreadNum清0,则认为超时,返回false,可以根据实际情况选择处理或忽视
runningThreadNum.await(1, TimeUnit.MINUTES);
} catch (InterruptedException e) {
//此处简化处理,非正常中断应该抛出异常或返回错误结果
return null;
}
}
return countDownResultList;
} /**
* 线程请求处理类
*
*/
private class DealWorker implements Runnable { /** 正在运行的线程数 */
private CountDownLatch runningThreadNum; /**待处理请求*/
private DealRequest request; /**待返回结果列表*/
private List<DealResult> countDownResultList; /**
* 构造函数
* @param request 待处理请求
* @param runningThreadNum 正在运行的线程数
* @param countDownResultList 待返回结果列表
*/
private DealWorker(DealRequest request, CountDownLatch runningThreadNum, List<DealResult> countDownResultList) {
this.request = request;
this.runningThreadNum = runningThreadNum;
this.countDownResultList = countDownResultList;
} @Override
public void run() {
try{
this.countDownResultList.add(process(this.request));
}finally{
//当前线程处理完成,runningThreadNum线程数减1,此操作必须在finally中完成,避免处理异常后造成runningThreadNum线程数无法清0
this.runningThreadNum.countDown();
}
}
}
是不是很简单?下图和上面的代码又做了一个对应,假设有3个请求,则启动3个子线程DealWorker,并实例化值数等于3的CountDownLatch。每当一个子线程处理完成后,则调用countDown操作减1。主线程处于awaiting状态,直到CountDownLatch的值数减到0,则主线程继续resume执行。
在API中是这样描述的:
用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。
经典的java并发编程实战一书中做了更深入的定义:CountDownLatch属于闭锁的范畴,闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态。闭锁的作用相当于一扇门:在闭锁到达结束状态之前(上面代码中的runningThreadNumq清0),这扇门一直是关闭的,并且没有任何线程能通过(上面代码中的主线程一直await),当到达结束状态时,这扇门会打开并允许所有线程通过(上面代码中的主线程可以继续执行)。当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态。
像FutureTask,Semaphore这类在concurrent包里的类也属于闭锁,不过它们和CountDownLatch的应用场景还是有差别的,这个我们在后面的文章里再细说。
使用CountDownLatch有哪些需要注意的点
- 批次请求之间不能有执行顺序要求,否则多个线程并发处理无法保证请求执行顺序
- 各线程都要操作的结果列表必须是线程安全的,比如上面代码范例的countDownResultList
- 各子线程的countDown操作要在finally中执行,确保一定可以执行
- 主线程的await操作需要设置超时时间,避免因子线程处理异常而长时间一直等待,如果中断需要抛出异常或返回错误结果
使用CountDownLatch提高批次处理速度的问题
- 如果一个批次请求数很多,会瞬间占用服务器大量线程。此时必须使用线程池,并限定最大可处理线程数量,否则服务器不稳定性会大福提升。
- 主线程和子线程间的数据传输变得困难,稍不注意会造成线程不安全的问题,且代码可读性有一定下降
下一篇文章我们讲讲FutureTask的应用场景,谢谢!
用CountDownLatch提升请求处理速度的更多相关文章
- 【转】通过CountDownLatch提升请求处理速度
countdownlatch是java多线程包concurrent里的一个常见工具类,通过使用它可以借助线程能力极大提升处理响应速度,且实现方式非常优雅.今天我们用一个实际案例和大家来讲解一下如何使用 ...
- 详解服务器性能测试的全生命周期?——从测试、结果分析到优化策略(转载)
服务器性能测试是一项非常重要而且必要的工作,本文是作者Micheal在对服务器进行性能测试的过程中不断摸索出来的一些实用策略,通过定位问题,分析原因以及解决问题,实现对服务器进行更有针对性的优化,提升 ...
- Web服务端性能提升实践
随着互联网的不断发展,日常生活中越来越多的需求通过网络来实现,从衣食住行到金融教育,从口袋到身份,人们无时无刻不依赖着网络,而且越来越多的人通过网络来完成自己的需求. 作为直接面对来自客户请求的Web ...
- 【性能压测】:MQ队列异步处理机制导致的系统无法接受请求的问题
一,最近压测系统交易峰值时,因该支交易采用MQ异步队列处理机制:该增加积分的交易,前段服务器优先返回给客户增加积分成功的结果,后端的MQ队列服务器再慢慢处理该请求: 二,压测过程中出现的问题现象:前几 ...
- JAVA RPC (七) 手把手从零教你写一个生产级RPC之client请求
上节说了关于通用请求代理,实际上对spring的bean引用都是通过koalasClientProxy来实现的,那么在代理方法中才是我们实际的发送逻辑,咱们先看一下原生的thrift请求是什么样的. ...
- Nginx 限制并发连接和并发请求数配置
Nginx限制并发连接和并发请求数配置 by:授客 QQ:1033553122 测试环境 nginx-1.10.0 配置介绍 查看是否内置模块 # pwd /mnt/nginx-1.10.0 ...
- Web 请求之--性能相关
本博客代码运行环境 python : Python 3.7.1rc1 version pip : pip 19.1.1 version Scrapy: scrapy 1.6.0 version asy ...
- Java 面试知识点【背诵版 240题 约7w字】
-- 转载自牛客网 是瑶瑶公主吖 Java 基础 40 语言特性 12 Q1:Java 语言的优点? ① 平台无关性,摆脱硬件束缚,"一次编写,到处运行". ② 相对安全的内存管理 ...
- 详解MySQL大表优化方案( 转)
当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...
随机推荐
- IDEA使用笔记(八)——自动生成 serialVersionUID 的设置
这个设置比较简单,也有一些博文已经写到了,为什么我还要写哪?(潜台词:因为我想凑一篇博文)我觉得学习,特别是编程学习是需要重复造轮子的,另外,就是加深自己的印象方便自己的查找.还有就是关键点,有些博客 ...
- Winform开发框架之图表报表在线设计器2-图表-SNF.EasyQuery项目--SNF快速开发平台3.3-Spring.Net.Framework
上一篇讲到,如何快速创建报表程序了.这篇教大家如何快速制作图表报表. 继上一篇,Winform开发框架之图表报表在线设计器-报表 上一篇讲到如何了创建数据源,这里就不在介绍了.那我们就直接从图表设计器 ...
- Apache Spark 2.3.0 正式发布
本章内容: 待整理 参考文献: Spark Release 2.3.0
- Kubernetes1.2如何使用iptables
转:http://blog.csdn.net/horsefoot/article/details/51249161 本次分析的kubernetes版本号:v1.2.1-beta.0. Kubernet ...
- Atitit 纯java项目的提升进度大法---通用json dsl接口
Atitit 纯java项目的提升进度大法---通用json dsl接口 1. Json dsl接口1 1.1. Url: http://aaa.com/api_jsondsl?dsl={}1 1. ...
- 【iCore4 双核心板_ARM】例程十一:DMA实验——存储器到存储器的传输
实验原理: DAM(直接存储器访问)传输不需要占用CPU,可以在存储器至存储器实现高速的数据 传输.本实验采用DAM2控制器的数据流0,选用通道0进行数据传输.通过LED的颜色来 判断传输是否成功. ...
- 【Unity】EasyTouch5触屏检测
Unity AssetStore地址 https://assetstore.unity.com/packages/tools/input-management/easy-touch-5-touc ...
- debian/deepin 15.3 15.4安装jdk 1.7 (或jdk 7),配置默认环境
一.前言 Deepin 15.3是基于Debian开发的,安装jdk 1.7有所不同,默认是openjdk-8-jdk,而我们玩一些编译需要的是jdk 7. 所以本文给出安装JDK 7的教程. Dee ...
- 初试 Kubernetes 集群中使用 Traefik 反向代理
初试 Kubernetes 集群中使用 Traefik 反向代理 2017年11月17日 09:47:20 哎_小羊_168 阅读数:12308 版权声明:本文为博主原创文章,未经博主允许不得转 ...
- precision scale
precision意为“精密度.精确”(精度),表示该字段的有效数字位数了. scale意为“刻度.数值范围”(),表示该字段的小数位数. 举个简单的例子 123.45:precision = 5 , ...