教校花学妹JDK批量异步任务最强工具CompletionService

简介: 教校花学妹JDK批量异步任务最强工具CompletionService

如何优化一个查询各个价格接口的代码?若使用“ThreadPoolExecutor+Future”,可能优化如下:

三个线程异步执行查询价格,通过三次调用Future的get()方法获取结果,之后将查询结果保存在MySQL。

1.png若获取price1耗时很长,那么即便获取price2耗时短,也无法让保存price2的操作先执行,因为主线程都阻塞在 f1.get()。这种问题如何解决呢?

image.png

加个阻塞队列!

获取到price1、2、3都进入阻塞队列,然后在主线程消费阻塞队列,就能保证先获取到的价格先保存:

image.png

CompletionService实现查询价格

实际开发推荐CompletionService,不但能帮你解决先获取到的价格先保存,还能精简代码。


CompletionService内部维护了一个阻塞队列,当任务执行结束就把任务的执行结果入队,但CompletionService是把任务执行结果的Future对象入队,而上面demo是把任务最终执行结果入队。

创建CompletionService

CompletionService接口的实现类是ExecutorCompletionService,这个实现类的构造方法有两个,分别是:

  • ExecutorCompletionService(Executor executor);

image.png

  • ExecutorCompletionService(Executor executor, BlockingQueue<Future> completionQueue)

image.png

这俩构造器都需要传入一个线程池,若不指定completionQueue,默认使用无界LinkedBlockingQueue。任务执行结果的Future对象就是加入到completionQueue中。


让我们试着利用CompletionService实现高性能的查询房价系统。

之后通过CompletionService#submit()提交三个询价操作,这三个询价操作将会被CompletionService异步执行。


最后CompletionService#take()获取一个Future对象(加入到阻塞队列的是任务执行结果的Future对象),调用Future#get()就能返回执行结果。

image.png

CompletionService接口

CompletionService接口提供的方法

image.png

submit()相关的方法有两个:


  • 一个方法参数是Callable<V> task
  • 一个方法有两个参数,分别是Runnable task和V result,该方法类似于ThreadPoolExecutor的 <T> Future<T> submit(Runnable task, T result) ,

CompletionService实现Dubbo#Forking Cluster

Dubbo中有一种叫做Forking的集群模式,这种集群模式下,支持并行调用多个查询服务,只要有一个成功返回结果,整个服务即可返回。例如你需要提供一个地址转坐标的服务,为了保证该服务的高可用和性能,可并行调用3个地图服务商的API,然后只要有1个正确返回了结果r,那么地址转坐标这个服务就可以直接返回r了。这种集群模式可以容忍2个地图服务商服务异常,但缺点是消耗的资源偏多。


/

geocoder(addr) {
  // 并行执行以下3个查询服务, 
  r1=geocoderByS1(addr);
  r2=geocoderByS2(addr);
  r3=geocoderByS3(addr);
  // 只要r1,r2,r3有一个返回
  // 则返回
  return r1|r2|r3;
}

利用CompletionService可快速实现 Forking 这种集群模式,比如下面示例代码。

首先创建一个线程池executor 、一个CompletionService对象cs和一个Future<Integer>类型的列表 futures,每次通过调用CompletionService的submit()方法提交一个异步任务,会返回一个Future对象,把这些Future对象保存在列表futures中。通过调用 cs.take().get(),我们能够拿到最快返回的任务执行结果,只要我们拿到一个正确返回的结果,就可以取消所有任务并且返回最终结果了。

// 创建线程池
ExecutorService executor =
  Executors.newFixedThreadPool(3);
// 创建CompletionService
CompletionService<Integer> cs =
  new ExecutorCompletionService<>(executor);
// 用于保存Future对象
List<Future<Integer>> futures =
  new ArrayList<>(3);
// 提交异步任务,并保存future到futures 
futures.add(
  cs.submit(()->geocoderByS1()));
futures.add(
  cs.submit(()->geocoderByS2()));
futures.add(
  cs.submit(()->geocoderByS3()));
// 获取最快返回的任务执行结果
Integer r = 0;
try {
  // 只要有一个成功返回,则break
  for (int i = 0; i < 3; ++i) {
    r = cs.take().get();
    // 简单地通过判空来检查是否成功返回
    if (r != null) {
      break;
    }
  }
} finally {
  // 取消所有任务
  for(Future<Integer> f : futures)
    f.cancel(true);
}
// 返回结果
return r;

总结

当需要批量提交异步任务,推荐CompletionService。CompletionService将线程池Executor和阻塞队列融合,让批量异步任务管理更简单。


CompletionService能让异步任务的执行结果有序化,先执行完的先进入阻塞队列,利用该特性,可以轻松实现后续处理的有序性,避免无谓等待,同时还可以快速实现诸如Forking Cluster这样的需求。


CompletionService的实现类ExecutorCompletionService,需要你自己创建线程池,虽看上去有些啰嗦,但好处是你可以让多个ExecutorCompletionService的线程池隔离,这种隔离性能避免几个特别耗时的任务拖垮整个应用的风险。


目录
相关文章
|
监控 数据可视化 Java
深入理解JVM系列教程(09) - JDK可视化工具
深入理解JVM系列教程(09) - JDK可视化工具
117 1
|
Java 关系型数据库 MySQL
mac,linux环境的基础工具安装【jdk,tomcat】
mac,linux环境的基础工具安装【jdk,tomcat】
241 1
|
安全 Java Shell
JDK9特性——模块化&REPL工具
谈到Java9大家往往第一个想到的就是Jigsaw项目(后改名为Modularity)。众所周知,Java已经发展超过20年(95年最初发布),Java和相关生态在不断丰富的同时也越来越暴露出一些问题:
233 0
|
监控 IDE Java
【Java性能调优新工具】JDK 22性能分析器:深度剖析,优化无死角!
【9月更文挑战第9天】JDK 22中的性能分析器为Java应用的性能调优提供了强大的支持。通过深度集成、全面监控、精细化分析和灵活报告生成等核心优势,性能分析器帮助开发者实现了对应用性能的全面掌控和深度优化。在未来的Java开发过程中,我们期待性能分析器能够继续发挥重要作用,为Java应用的性能提升贡献更多力量。
|
Java jenkins 持续交付
Jenkins全局工具配置(JDK、Maven、Maven插件)
Jenkins全局工具配置(JDK、Maven、Maven插件)
1135 0
|
Arthas Prometheus 监控
使用JDK自带工具调优JVM的常用命令
使用JDK自带工具调优JVM的常用命令
166 0
|
监控 算法 Java
怎么用JDK自带工具进行JVM内存分析
JVM内存分析工具,如`jps`、`jcmd`、`jstat`、`jstack`和`jmap`,是诊断和优化Java应用的关键工具。`jps`列出Java进程,`jcmd`执行诊断任务,如查看JVM参数和线程堆栈,`jstat`监控内存和GC,`jstack`生成线程堆栈信息,而`jmap`则用于生成堆转储文件。这些工具帮助排查内存泄漏、优化内存配置、性能调优和异常分析。例如,`jmap -dump:file=heapdump.hprof &lt;PID&gt;`生成堆转储文件,之后可以用Eclipse Memory Analyzer (MAT)等工具分析。
450 0
|
Java iOS开发 MacOS
使用JDK自带的keytool工具生成签名文件
使用JDK自带的keytool工具生成签名文件
196 0
|
安全 Java API
jdk8获取当前时间|时间加减|java8时间格式化|时间处理工具|时间比较|线程安全的时间处理方法
jdk8获取当前时间|时间加减|java8时间格式化|时间处理工具|时间比较|线程安全的时间处理方法
|
Java 编译器 开发工具
JAVA工具-JDK、JRE、JVM、JIT
JAVA工具-JDK、JRE、JVM、JIT
233 0