用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单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...
随机推荐
- Axure RP for Mac(网站交互式原型设计工具)破解版安装
1.软件简介 Axure RP 是 macOS 系统上一款最知名和最强大的原型设计工具,增加了大量新的特性,如应用多个动画,并同一时间运行一个小部件,如褪色,同时移动等,而且具有全新的图标和界面 ...
- Redis高可用详解:持久化技术及方案选择
文章摘自:https://www.cnblogs.com/kismetv/p/9137897.html 前言 在上一篇文章中,介绍了Redis的内存模型,从这篇文章开始,将依次介绍Redis高可用相关 ...
- Atitit 类库冲突 解决方案
Atitit 类库冲突 解决方案 表现情况,找到不某些方法 类等,一个情况是真的找不到,一个情况是加载了错误的jar,导致正确的jar无法加载.. 1.1. 查找现在ide正在使用的jar1 1.2. ...
- linux每日命令(39):lsof命令
lsof(list open files)是一个列出当前系统打开文件的工具.在linux环境下,任何事物都以文件的形式存在,通过文件不仅仅可以访问常规数据,还可以访问网络连接和硬件.所以如传输控制协议 ...
- 【iCore1S 双核心板_ARM】例程一:ARM驱动三色LED
实验原理: 通过STM32的三个GPIO驱动一个三色LED,引脚PF3接蓝色LED(ARM_LEDB), 引脚PF4接绿色LED(ARM_LEDG),引脚PF5接红色LED(ARM_LEDR), G ...
- android 自定义无限循环播放的viewPager。轮播ViewPager。实现循环播放 广告,主题内容,活动,新闻内容时。
前言 实际项目需要一个 播放广告的控件,可能有多个广告图片.每个一段时间更换该图片.简单来说,就是一个 “循环播放图片”的控件. 间隔时间更换图片 一般来说,图片切换时需要有动画效果 需要支持手势,用 ...
- JavaScript高级用法一之事件响应与网页交互
综述 本篇的主要内容来自慕课网,事件响应与网页交互,主要内容如下 1 什么是事件 2 鼠标单击事件( onclick ) 3 鼠标经过事件(onmouseover) 4 鼠标移开事件(onmouseo ...
- prototype [ˈprəʊtətaɪp] 原型
<script> Array.prototype.mysort = function(){ let s = this; for(i=0;i<s.length;i++){ s[i] = ...
- 汽车行业与 Telematics
Telematics Telematics是远距离通信的电信(Telecommunications)与信息科学(Informatics)的合成词,按字面可定义为通过内置在汽车.航空.船舶.火车等运输工 ...
- java-信息安全(十二)-数字签名【Java证书体系实现】
概述 信息安全基本概念 数字证书 数字证书就是互联网通讯中标志通讯各方身份信息的一串数字,提供了一种在Internet上验证通信实体身份的方式,数字证书不是数字身份证,而是身份认证机构盖在数字身份证上 ...