CompletableFuture如何优雅处理异步任务超时!妙就完了

文章目录

  • 1. 主要解决哪些业务痛点?
  • 2. 流程分析
  • 3. 上代码
  • 4. 总结一波

1. 主要解决哪些业务痛点?

小强最近一直没打黑神话悟空,闷闷不乐的,我问咋回事,最近有啥烦心事么?

他不爽的跟我说了当他CompletableFuture进行任务编排时,会发现一个问题,当一个子线程去执行任务,如果任务执行时间很长,导致后面的任务一直阻塞,他在想有没有一种办法,让子线程具有等待超时的特性。

小强对编程的热情确实是高,那我们给他一起分析一下!!

其实在CompletableFuture中,提供了CompletableFuture.get(long timeout, TimeUnit unit) 方法,可以设置超时等待时间,但是这个是对于主线程而言的,在java8中,子线程是没有办法去设置等待超时时间的

其实通俗来讲就是:

就是调用CompletableFuture.supplyAsync()相关方法时,不能够传入子线程的等待时间,因为在很多时候会遇到使用上的一些拘束:

为了让大家了解更清楚,我们带着这个问题去看一个场景题:

接下来看一个场景:

2. 流程分析

流程大体如下:

  1. 我们的主线程会同时起一个异步线程1和异步线程2,并且异步线程1会执行任务A,异步线程2会执行B。
  2. 任务A和任务B的执行时间时不确定的,可能是1秒或者是20秒。但是我们的异步线程只会等待两秒中,如果没执行完,对于A会执行兜底任务B,对于C会执行兜底任务D。

可以思考下:

那我们如何实现子线程的等待超时,可能一下子会去想到CompletableFuture.get(),但是这个是对于主线程而言的,阻塞的粒度太粗了,那如何把超时等待下放到每一个子线程去独立控制呢?

整体思路:

  1. 异步的超时控制,比如定时3s钟,肯定在3秒钟会检查任务是否执行完,肯定会有一个定时器对象到3秒钟去检查。
  2. 检查完之后,如果任务还没完成,不需要等待,直接返回默认值,走接下来的逻辑。

3. 上代码

代码搞起:

并发引擎工具类如下:

public class CompletableFutureTimeoutEngine {static ScheduledThreadPoolExecutor delayer;static ExecutorService executor = Executors.newCachedThreadPool();//定义一个延迟任务器,用来设置执行超时时间后需要执行的任务
static {delayer = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);t.setDaemon(true);t.setName("CompletableFutureDelayScheduler");return t;}});delayer.setRemoveOnCancelPolicy(true);
}//当超时时间到时,需要抛出超时异常
public static <T> CompletableFuture<T> timeoutAfter(long timeout, TimeUnit unit) {CompletableFuture<T> result = new CompletableFuture<T>();delayer.schedule(() -> result.completeExceptionally(new TimeoutException()), timeout, unit);return result;
}public static <T> CompletableFuture<T> completeOnTimeout(Supplier<T> supplier, long timeout, TimeUnit unit, Function<Throwable, T> function) {return completeOnTimeout(supplier, timeout, unit, function, null);
}/*** @param t                超时默认返回值* @param supplier         任务* @param timeout          超时时间* @param unit             事件单位* @param throwableHandler 异常处理* @param <T>              任务的类型* @return 任务的返回CompletableFuture<T>*/
public static <T> CompletableFuture<T> completeOnTimeout(Supplier<T> supplier, long timeout,TimeUnit unit, Function<Throwable, T> throwableHandler, T t) {return CompletableFuture.supplyAsync(supplier, executor).applyToEither(timeoutAfter(timeout, unit), Function.identity()).exceptionally((throwable) -> {Throwable cause = throwable.getCause();if (cause instanceof TimeoutException) {return t;}return throwableHandler.apply(cause);});
}}

上面主要定义了一个延迟任务器,主要用来执行超时时间后需要执行的任务,在指定的超时时间到达时,会在timeoutAfter方法抛出超时异常,主方法completeOnTimeout用来捕获是否是超过超时时间抛出的超时异常,如果是,则返回默认值,如果不是,执行正常任务的返回。

接下来,我们去调用一下:

public class TimeoutTest {private static void sleep(Long time) {try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {CompletableFuture<Integer> completableFutureA = CompletableFutureTimeoutEngine.completeOnTimeout(() -> {sleep(4000L);return 1;}, 2, TimeUnit.SECONDS, (throwable -> {throw new BusinessException("111");}), null);CompletableFuture<Integer> completableFutureB = completableFutureA.thenApply(s -> {if (s == null) {//处理B任务System.out.println("任务A超时,执行B");sleep(1000L);return 1;}return s;});CompletableFuture<Integer> completableFutureC = CompletableFutureTimeoutEngine.completeOnTimeout(() -> {sleep(5000L);return 10;}, 3, TimeUnit.SECONDS, (throwable -> {throw new BusinessException("111");}), null);CompletableFuture<Integer> completableFutureD = completableFutureC.thenApply(s -> {if (s == null) {//处理D任务System.out.println("任务C超时,执行D");sleep(1000L);return 2;}return s;});CompletableFuture<Integer> future = completableFutureB.thenCombine(completableFutureD, Integer::sum);Integer result = future.get(6, TimeUnit.SECONDS);System.out.println("sum = " + result);}}

这个demo的实行顺序就是 completableFutureA -> completableFutureB 和 completableFutureC -> completableFutureD 分别并行执行,future最后拿到指定的结果和。

去执行一下看看:

你会发现completableFutureA的超时等待时间是2秒,正常的任务执行是4s,因此返回超时默认值null,用于和completableFutureB做处理,completableFutureB判断传进来是null,返回值为1。

同样的,completableFutureC的超时等待时间是3秒,正常的任务执行是5s,因此返回超时默认值null,用于和completableFutureD做处理,completableFutureD判断传进来是null,返回值为2。

future对completableFutureB和completableFutureD做聚合,值为3。

4. 总结一波

上面的代码就实现了completableFuture子线程具有超时的特性。是不是看完之后恍然大悟,其实很多中间件都是基于java基础做的。

但是这种方式在并发不高的情况下,可以让作为工具类控制并发流转,但是在并发很高的情况下,定时器机制可能是一个瓶颈,需要换成时间轮或者在业务里面处理超时,具体看自己的业务场景。

感觉对您有所启发的话,记得帮忙点赞,收藏加关注,并且分享给需要的小伙伴!!加油!!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

css基础知识笔记

一言&#xff1a; “放任误解就是撒谎。” 文章目录 前言文章有误敬请斧正 不胜感恩&#xff01;CSS基础教程0.文本样式基础1. CSS选择器2. CSS布局技巧3. 响应式设计4. Emmet语法 总结 前言 写在开始&#xff1a; 今天来看一眼CSS基础知识。 好几天没更新了 先更一篇 文章有…

华为OD机试 - 需要打开多少监控器(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

2024年最新网络协议分析器Wireshark抓包详细教程(更新中)

网络协议分析器 Wireshark 安装 Wireshark 是一个功能强大的网络协议分析器&#xff0c;早期叫作 Ethereal。它主要用于捕获网络数据包&#xff0c;并对这些数据包进行详细的解析和分析&#xff0c;帮助用户深入了解网络通信的细节。它支持多种网络协议&#xff0c;并提供详细…

银河麒麟桌面操作系统如何添加WPS字体

银河麒麟桌面操作系统如何添加WPS字体 1、使用场景2、操作方法步骤一&#xff1a;下载字体文件步骤二&#xff1a;打开终端步骤三&#xff1a;进入字体文件所在目录步骤四&#xff1a;拷贝字体文件到WPS字体目录步骤五&#xff1a;更新字体缓存步骤六&#xff1a;重启WPS Offic…

uni-app-通过vue-cli命令行快速上手

环境安装 全局安装 vue-cli npm install -g vue/cli创建uni-app 使用正式版&#xff08;对应HBuilderX最新正式版&#xff09; vue create -p dcloudio/uni-preset-vue my-project使用alpha版&#xff08;对应HBuilderX最新alpha版&#xff09; vue create -p dcloudio/uni-p…

Linux常用命令;Linux常用软件;Linux权限

一&#xff0c;常用命令 是人向计算机发送指令的语言。 命令的格式&#xff1a; 命令 [选项] [参数] 1、ls 展示当前目录下文件的命令 1、-l 展示详细信息。还有另外一种写法&#xff1a;ll&#xff08;字母 LL 小写&#xff09; 2、-S 按照文件大小倒序展示 3、-t…

1952. 三除数

目录 一&#xff1a;题目&#xff1a; 二&#xff1a;代码&#xff1a; 三&#xff1a;结果&#xff1a; 一&#xff1a;题目&#xff1a; 给你一个整数 n 。如果 n 恰好有三个正除数 &#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在整数 k &a…

【软件测试】性能测试-概念篇

目录 &#x1f334;什么是性能测试 &#x1f333;常见性能测试指标 &#x1f6a9;并发数 &#x1f6a9;吞吐量 &#x1f6a9;吞吐量分类 &#x1f3c0;按照请求分类:TPS和QTS &#x1f3c0;按照网络数据包划分:KB &#x1f6a9;响应时间 &#x1f6a9;资源利用率 &am…

SpringBoot启动流程之运行时监听器

SpringBoot启动过程&#xff1a; 上一节我们讨论SpringApplication实例化的过程&#xff0c;也就是上图1-5步骤&#xff0c;本节我们讨论6-9的关键步骤&#xff0c;现在主要讲是run方法里面的过程 /*** 启动方法* param args* return*/public ConfigurableApplicationContext …

基于JAVA+SpringBoot+Vue的景区民宿预约系统

基于JAVASpringBootVue的景区民宿预约系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末附源码下载链接&#x1f345; 哈…

Mamba所需的causal-conv1d 和mamba-ssm库在哪下载?

背景介绍 参照 Mamba [state-spaces/mamba: Mamba SSM architecture (github.com)] github中提到的环境安装[Installation 一栏] [Option] pip install causal-conv1d>1.4.0: an efficient implementation of a simple causal Conv1d layer used inside the Mamba block.…

浙版传媒思迈特软件大数据分析管理平台建设项目正式启动

近日&#xff0c;思迈特软件与出版发行及电商书城领域的领军企业——浙江出版传媒股份有限公司&#xff0c;正式启动大近日&#xff0c;思迈特软件与出版发行及电商书城领域的领军企业——浙江出版传媒股份有限公司&#xff0c;正式启动大数据分析管理平台建设项目。浙版传媒相…

华为HarmonyOS灵活高效的消息推送服务(Push Kit) - 2 开通推送服务与配置Client ID

在开通推送服务前&#xff0c;请先参考“应用开发准备”完成基本准备工作&#xff0c;再继续进行以下开发活动。 说明 从HarmonyOS NEXT Developer Beta2起&#xff0c;开发者无需配置公钥指纹和Client ID。 操作步骤 登录AppGallery Connect网站&#xff0c;选择“我的项目…

UML图中部署图例题

答案&#xff1a;B 知识点&#xff1a; 组件图 一组构件之间的组织和依赖&#xff0c;专注于系统的静态实现视图 部署图 运行处理结点以及构件的配置&#xff0c;给出体系结构的静态视图 类图 一组对象&#xff0c;接口&#xff0c;协作和它们之间的关系 UML图中涉及到…

ALTIUM DESIGNER PCB设计中关闭和打开捕捉热点(hot spot)功能

ALTIUM DESIGNER PCB设计中关闭和打开捕捉热点&#xff08;snap to hot spot&#xff09;功能 在采用ALTIUM DESIGNER 18 进行PCB元器件布局时&#xff0c;我喜欢将元器件放置在栅格&#xff08;grid&#xff09;上&#xff0c;这样元器件的位置比较规整。但在设置完栅格后&am…

Java流程控制语句——跳转语句详解:break 与 continue 有什么区别?

&#x1f310;在Java编程中&#xff0c;break和continue是两个重要的控制流语句&#xff0c;它们允许开发者根据特定条件改变程序的执行流程。虽然两者都用于中断当前的行为&#xff0c;但它们的作用方式不同。本文将通过生动的例子来详细解释这两个语句&#xff0c;并使用流程…

VMware启动时报错: “另一个程序已锁定文件的一部分,进程无法访问” 分析记录

项目场景&#xff1a; VMware启动时报错: “另一个程序已锁定文件的一部分,进程无法访问” 问题描述 VMware启动时报错: “另一个程序已锁定文件的一部分,进程无法访问” 原因分析&#xff1a; 虚拟机开启后会对部分文件继续加密&#xff0c;关闭时虚拟机会自动对其解密&…

计算机毕业设计之:基于uni-app的校园活动信息共享系统设计与实现(三端开发,安卓前端+网站前端+网站后端)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Transcipher:从对称加密到同态加密

摘要 本文介绍了Transcipher的概念。在Transcipher的框架下&#xff0c;用户使用高效的对称加密&#xff0c;对自己的数据进行加密&#xff0c;然后将密文和私钥的同态加密密文传输给服务器。服务器进行同态解密&#xff0c;得到用户数据同态加密的密文。Transcipher通过将计算…

分布式锁的几种方案对比?你了解多少种呢?

目录标题 1.关于分布式锁2.分布式锁的实现方案2.1 基于数据库实现2.1.1乐观锁的实现方式2.1.2 悲观锁的实现方式2.1.3 数据库锁的优缺点 2.2 基于Redis实现2.2.1 基于缓存实现分布式锁2.2.2缓存实现分布式锁的优缺点 2.3 基于Zookeeper实现2.3.1 如何实现&#xff1f;2.3.2 zk实…