ExecutorCompletionService详解

本文已收录至Github,推荐阅读 👉 Java随想录

微信公众号:Java随想录

文章目录

    • 摘要
    • ExecutorCompletionService适用场景
    • ExecutorCompletionService使用
    • ExecutorCompletionService原理解析
    • 注意事项
    • 总结

摘要

ExecutorCompletionService 是Java并发编程中的一个有用的工具类,它实现了 CompletionService 接口。ExecutorCompletionService 将 Executor 和BlockingQueue 功能融合在一起,使用它可以提交我们的任务。这个任务委托给 Executor 执行,可以使用 ExecutorCompletionService 对象的 take() 和 poll() 方法获取结果。

本文将深入讲解 ExecutorCompletionService 的使用以及源码解析。

ExecutorCompletionService适用场景

ExecutorCompletionService在以下场景中特别有用:

  • 并行任务处理:当需要同时执行多个任务,并按照完成的顺序获取它们的结果时,可以使用ExecutorCompletionService来简化任务提交和结果获取的流程。
  • 高性能计算:在需要进行大规模计算或复杂计算的场景中,可以将任务拆分成多个子任务,并使用ExecutorCompletionService来管理和获取子任务的结果。

假设现在有一批需要进行计算的任务,为了提高整批任务的执行效率,我们可以使用线程池来异步计算这些任务。通过向线程池中不断提交任务并保留与每个任务关联的Future对象。最后,我们可以遍历这些Future对象,并通过调用 get() 方法获取每个任务的计算结果。

Future的不足

Future 没有办法回调,只能手动去调用,当通过 get() 方法获取线程的返回值时,会导致阻塞,也就是和当前这个 Future 关联的计算任务执行完成的时候才返回结果,新任务必须等待已完成任务的结果才能继续进行处理。

这样会浪费很多时间,因为我们不知道哪个线程先执行完了,只能挨个去获取结果,这样已经完成的线程会因为前面未完成的线程的耗时而无法提前进行汇总,最好是谁先执行完成,谁先返回。

而 ExecutorCompletionService 可以实现这样的效果,节省获取完成结果的时间,它的内部有一个先进先出的阻塞队列,用于保存已经执行完成的 Future,通过调用它的 take() 方法或 poll() 方法可以获取到一个已经执行完成的 Future,进而通过调用 Future 接口实现类的 get() 方法获取最终的结果。

CompletionService的目标是任务谁先完成谁先获取,即结果按照完成先后顺序排序

ExecutorCompletionService使用

ExecutorCompletionService 提供了一种方便的方式来处理一组异步任务,并按照完成的顺序获取它们的结果。它内部使用了Executor框架来执行任务,并且内部管理着一个已完成任务的阻塞队列,在结果获取上提供了更加灵活和高效的机制。

下面是一个简单的例子来演示ExecutorCompletionService的基本使用:

public class ExecutorCompletionServiceExample {public static void main(String[] args) throws InterruptedException, ExecutionException {ExecutorService executor = Executors.newFixedThreadPool(5);CompletionService<String> completionService = new ExecutorCompletionService<>(executor);// 提交任务for (int i = 0; i < 10; i++) {final int taskId = i;completionService.submit(() -> {double sleepTime = Math.random() * 1000;Thread.sleep((long) sleepTime); // 模拟耗时操作return "Task " + taskId + " completed,cost time: " + sleepTime;});}// 获取结果for (int i = 0; i < 10; i++) {Future<String> future = completionService.take();String result = future.get();System.out.println(result);}executor.shutdown();}
}

输出:

Task 2 completed,cost time: 170.01927312611775
Task 3 completed,cost time: 460.9622858036789
Task 1 completed,cost time: 563.24738180643
Task 0 completed,cost time: 595.938819219159
Task 5 completed,cost time: 480.4473056068137
Task 4 completed,cost time: 748.2343208613524
Task 6 completed,cost time: 370.4679098376097
Task 7 completed,cost time: 270.45945981324905
Task 9 completed,cost time: 336.5536570760892
Task 8 completed,cost time: 577.5774464801026

在上述代码中,我们创建了一个固定大小的线程池,并使用 ExecutorCompletionService 来提交和获取任务的结果。通过调用completionService.submit()方法来提交任务,并随机指定睡眠时间,来模拟任务执行的耗时,然后通过completionService.take()方法来获取已完成的任务结果。

可以看到是按照任务的执行耗时顺序去获取结果的。

ExecutorCompletionService原理解析

ExecutorCompletionService 提供了两个构造函数,一个可以指定阻塞队列,另一个使用内部默认的阻塞队列,两个构造函数都需要传进线程池参数。

提供了三个获取方法,可以看到都是从队列中获取。

  • take()/poll() 方法的工作都委托给内部的已完成任务队列 completionQueue。
  • 如果队列中有已完成的任务, take() 方法就返回任务的结果,否则阻塞等待任务完成。
  • poll() 与 take() 方法不同,poll() 有两个版本:
    • 无参的 poll() 方法:如果完成队列中有数据就返回,否则返回null。
    • 有参数的 poll() 方法:如果完成队列中有数据就直接返回,否则等待指定的时间,到时间后如果还是没有数据就返回null。

两个提交任务方法,可以看到 submit() 方法最终会委托给内部的 executor 去执行任务,提交任务的时候会将任务封装成 QueueingFuture 对象。

ExecutorCompletionService内部维护了 QueueingFuture 类,QueueingFuture 继承了 FutureTask,并重写了 done() 方法,

可以看到 done() 方法在任务完成的时候会将结果存进 已完成任务队列 completionQueue 中。

Futuretask 的 done() 方法是用来标记一个任务已经完成的方法。当一个 Futuretask 中的任务完成后,就会调用 done() 方法通知。

默认是空方法,不会执行任何动作。

执行流程

当我们使用ExecutorCompletionService类时,它能够按照任务完成的顺序获取它们的结果,这是因为ExecutorCompletionService类内部结合了QueueingFuture类和done()方法的机制。以下是源码流程步骤解释:

  1. 提交任务:
    • 我们通过submit方法将任务提交给ExecutorCompletionService。在提交任务时,ExecutorCompletionService会使用自定义的QueueingFuture类来包装任务,并将其交给底层线程池执行。
  2. QueueingFuture类:
    • QueueingFuture类是ExecutorCompletionService的内部类,继承自FutureTask。它的构造方法接收一个Callable对象作为参数。
    • 在QueueingFuture类中,它重写了done()方法。done()方法会在任务执行完成后被调用。
  3. 任务执行完成时的处理:
    • 当任务执行完成后,在底层线程池的Worker线程中,会调用QueueingFuture的done()方法。
    • 在done()方法中,QueueingFuture会首先调用父类FutureTask的done()方法,以触发对计算结果的获取。然后,它会将任务的结果存储到一个内部的BlockingQueue队列中(即completionQueue)。
  4. 获取任务结果:
    • 当我们调用take方法获取任务结果时,它会从completionQueue队列中取出已完成的任务结果,并返回该结果。如果队列为空,则会阻塞等待,直到有任务完成并返回结果。
    • take方法内部会调用QueueingFuture的get()方法,从而触发对应任务的计算结果的获取。
  5. 保证按顺序获取结果:
    • 由于completionQueue是一个阻塞队列,并且在done()方法中将任务结果按照完成的顺序放入队列中,因此我们可以通过按顺序获取队列中的任务结果,来保证按照任务完成的顺序获取它们的结果。

通过以上源码流程步骤,ExecutorCompletionService类能够按照任务完成的顺序获取结果。它利用QueueingFuture类包装任务并存储结果到阻塞队列中,在任务执行完成后,按照完成的顺序将结果放入队列,从而实现了按顺序获取结果的功能。

注意事项

在使用ExecutorCompletionService时,需要注意以下事项:

  • 合理选择线程池大小:根据任务的数量和复杂性,合理选择线程池的大小,以充分利用系统资源并避免资源浪费。
  • 及时处理异常:在任务执行过程中,如果发生异常,需要及时处理和记录异常信息,以保证程序的稳定性和可靠性。
  • 使用Future对象进行任务取消和超时控制:通过使用Future对象的cancel方法,可以取消正在执行的任务。同时,可以通过调整 poll 方法的参数来设置超时时间,避免长时间等待任务结果而导致阻塞。

总结

ExecutorCompletionService是一个强大且灵活的工具类,能够简化异步任务的处理和结果获取过程。通过使用ExecutorCompletionService,我们可以更加高效地处理一组异步任务,并按照完成的顺序获取它们的结果。

本文介绍了ExecutorCompletionService的基本使用方法,并对其源码进行了解析。希望通过这篇博客能够帮助读者更好地理解和应用ExecutorCompletionService。

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

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

相关文章

Java原生启动Tomcat

文章目录 引入依赖启动Tomcat代码示例将嵌入式 Tomcat 服务器用于已有的 WAR 文件为现有的 Java Web 应用程序嵌入 Tomcat 服务器 相关APITomcat APIContonxt API 启动错误springboot底层Tomcat的实现学习博客 引入依赖 maven: <dependency><groupId>org.apache.…

如何在无公网IP环境使用Windows远程桌面Ubuntu

文章目录 一、 同个局域网内远程桌面Ubuntu二、使用Windows远程桌面连接三、公网环境系统远程桌面Ubuntu1. 注册cpolar账号并安装2. 创建隧道&#xff0c;映射3389端口3. Windows远程桌面Ubuntu 四、 配置固定公网地址远程Ubuntu1. 保留固定TCP地址2. 配置固定的TCP地址3. 使用…

48道Linux面试题

本博客将汇总 Linux 面试中常见的题目&#xff0c;并提供详细的解答。 文章目录 1、绝对路径用什么[符号表](https://so.csdn.net/so/search?q符号表&spm1001.2101.3001.7020)示&#xff1f;当前目录、上层目录用什么表示&#xff1f;主目录用什么表示? 切换目录用什么命…

DolphinScheduler实际应用

前言 最近公司新启动了一个项目&#xff0c;然后领导想用一下新技术&#xff0c;并且为公司提供多个大数据调度解决方案&#xff0c;我呢就根据领导要求调研了下当前的开源调度工具&#xff0c;最终决定采用DolphinScheduler&#xff0c; 因此研究了一下DolphinScheduler &…

php安装扩展event 提示 No package ‘openssl‘ found 解决方法

在使用pecl编译安装最新版event模块的时候提示 No package openssl found , 可是本机是安装了openssl的, 编译时找不到, 大概率就是环境配置的问题了, 增加 OPENSSL_CFLAGS OPENSSL_LIBS环境变量即可解决. 异常提示信息: checking for openssl > 1.0.2... no configure: …

阿里云服务器端口PPTP 1723放行教程

阿里云服务器安装PPTP VPN需要先开通1723端口&#xff0c;阿里云服务器端口是在安全组中操作的&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说明阿里云服务器安全组开放PPTP VPN专用1723端口教程&#xff1a; 阿里云服务器放行1723端口教程 PPTP是点对点隧道协议&#…

C++ 多态向下转型详解

文章目录 1 . 前言2 . 多态3 . 向下转型3.1 子类没有改进父类的方法下&#xff0c;去调用该方法3.2 子类有改进父类的方法下&#xff0c;去调用该方法3.3 子类没有改进父类虚函数的方法下&#xff0c;去调用改方法3.4 子类有改进父类虚函数的方法下&#xff0c;去调用改方法3.5…

接口测试场景:怎么实现登录之后,需要进行昵称修改?

在接口测试中有一个这样的场景&#xff1a;登录之后&#xff0c;需要进行昵称修改&#xff0c;怎么实现&#xff1f; 首先我们分别看下登录、昵称修改的接口说明&#xff1a; 以上业务中补充一点&#xff0c;昵称修改&#xff0c;还需要添加请求头Authorization传登录获取的to…

智慧监控平台/AI智能视频EasyCVR接口调用编辑通道详细步骤

视频监控TSINGSEE青犀视频平台EasyCVR能在复杂的网络环境中&#xff0c;将分散的各类视频资源进行统一汇聚、整合、集中管理&#xff0c;在视频监控播放上&#xff0c;GB28181视频安防监控汇聚平台可支持1、4、9、16个画面窗口播放&#xff0c;可同时播放多路视频流&#xff0c…

AI:106-基于卷积神经网络的遥感图像地物分类

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

解决Redis序列化乱码问题

如果我们使用原生的JDK序列化&#xff0c;那么当我们将数据存储到Redis中就会出现乱码的情况 为了解决这个问题我们需要重写RedisTemplate从而解决序列化乱码问题 首先在Maven中引入相应的依赖 <dependency> <groupId>com.fasterxml.jackson.core</group…

面向对象综合训练综合练习(文字版格斗游戏,对象数组,复杂的对象数组操作)

文章目录 练习一&#xff1a;文字版格斗游戏练习二&#xff1a;文字版格斗游戏进阶练习三&#xff1a;对象数组&#xff08;商品&#xff09;练习四&#xff1a;对象数组&#xff08;汽车&#xff09;练习五&#xff1a;对象数组&#xff08;手机&#xff09;练习六&#xff1a…

【Linux】深度解剖环境变量

> 作者简介&#xff1a;დ旧言~&#xff0c;目前大二&#xff0c;现在学习Java&#xff0c;c&#xff0c;c&#xff0c;Python等 > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;熟悉并掌握Linux的环境变量。 > 毒鸡汤&#x…

探索 3D 图形处理的奥秘

最近一年多来&#xff0c;在 3Dfx、Intel 们的狂轰滥炸中&#xff0c;在 Quake、古墓丽影们的推波助澜下&#xff0c;三维图形已经成为计算机迷眼中的又一个热点。3D 世界到底是怎样的神奇&#xff0c;我们又是怎样享受它的乐趣呢&#xff1f;就让我们来一探究竟吧。 图形基础…

流媒体学习之路(WebRTC)——GCC分析(4)

流媒体学习之路(WebRTC)——GCC分析&#xff08;4&#xff09; —— 我正在的github给大家开发一个用于做实验的项目 —— github.com/qw225967/Bifrost目标&#xff1a;可以让大家熟悉各类Qos能力、带宽估计能力&#xff0c;提供每个环节关键参数调节接口并实现一个json全配置…

视频剪辑方法:掌握视频嵌套合并技术,释放无限创意

随着数字媒体的普及&#xff0c;视频剪辑已是创意表达的重要技巧。通过掌握视频嵌套合并技术&#xff0c;可以将多个视频片段融合在一起&#xff0c;创造出独特的视觉效果和故事叙述。现在一起看云炫AI智剪批量剪辑视频嵌套合并方法&#xff0c;释放无限创意。 准备视频素材&a…

Certum ev多域名证书的优势

多域名证书作为一种能够为多个域名提供安全保护的证书类型&#xff0c;越来越受到企业的青睐。Certum作为一个成立了二十几年的CA认证机构&#xff0c;旗下的EV多域名SSL证书产品已经保护了多家企业的网站。Certum旗下的EV多域名证书作为一种能够为多个域名提供安全保护的证书类…

深度思考,AI项目的人工智能到底引领的是什么?

项目深度思考&#xff0c;人工智能到底引领的是什么&#xff1f; 人工智能引领技术之舞&#xff1a;项目深度思考项目背景&#xff1a;人工智能的魔法时代技术选择的深度思考&#xff1a;AI大决战团队协作的深度思考&#xff1a;AI联盟大会用户体验的深度思考&#xff1a;AI之光…

Redis(二)数据类型

文章目录 官网备注十大数据类型StringListHashSetZSetBitmapHyperLogLog&#xff1a;GEOStreamBitfield 官网 英文&#xff1a;https://redis.io/commands/ 中文&#xff1a;http://www.redis.cn/commands.html 备注 命令不区分大小写&#xff0c;key区分大小写帮助命令help…

DHCP定义

DHCP&#xff08;动态主机配置协议&#xff09;是一个局域网的网络协议。指的是由服务器控制一段IP地址范围&#xff0c;客户机登录服务器时就可以自动获得服务器分配的IP地址和子网掩码。默认情况下&#xff0c;DHCP作为Windows Server的一个服务组件不会被系统自动安装&#…