深入理解spring boot异步调用方式@Async

本文主要给大家介绍了关于spring boot异步调用方式@Async的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍:

1.使用背景

在日常开发的项目中,当访问其他人的接口较慢或者做耗时任务时,不想程序一直卡在耗时任务上,想程序能够并行执行,我们可以使用多线程来并行的处理任务,也可以使用spring提供的异步处理方式@Async。

2.异步处理方式

  • 调用之后,不返回任何数据。
  • 调用之后,返回数据,通过Future来获取返回数据

3.@Async不返回数据

使用@EnableAsync启用异步注解

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig{
}

在异步处理的方法dealNoReturnTask上添加注解@Async

@Component
@Slf4j
public class AsyncTask {

 @Async
 public void dealNoReturnTask(){
  log.info("Thread {} deal No Return Task start",Thread.currentThread().getName());
  try {
   Thread.sleep(3000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  log.info("Thread {} deal No Return Task end at {}",Thread.currentThread().getName(),System.currentTimeMillis());
 }
}

Test测试类:

@SpringBootTest(classes = SpringbootApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
@Slf4j
public class AsyncTest {

 @Autowired
 private AsyncTask asyncTask;

 @Test
 public void testDealNoReturnTask(){
  asyncTask.dealNoReturnTask();
  try {
   log.info("begin to deal other Task!");
   Thread.sleep(10000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }

日志打印结果为:

begin to deal other Task!
AsyncExecutorThread-1 deal No Return Task start
AsyncExecutorThread-1 deal No Return Task end at 1499751227034

从日志中我们可以看出,方法dealNoReturnTask()是异步执行完成的。

dealNoReturnTask()设置sleep 3s是为了模拟耗时任务

testDealNoReturnTask()设置sleep 10s是为了确认异步是否执行完成

4.@Async返回数据

异步调用返回数据,Future表示在未来某个点获取执行结果,返回数据类型可以自定义

 @Async
 public Future<String> dealHaveReturnTask() {
  try {
   Thread.sleep(3000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  JSONObject jsonObject = new JSONObject();
  jsonObject.put("thread",Thread.currentThread().getName());
  jsonObject.put("time",System.currentTimeMillis());
  return new AsyncResult<String>(jsonObject.toJSONString());
 }

测试类用isCancelled判断异步任务是否取消,isDone判断任务是否执行结束

 @Test
 public void testDealHaveReturnTask() throws Exception {

  Future<String> future = asyncTask.dealHaveReturnTask();
  log.info("begin to deal other Task!");
  while (true) {
   if(future.isCancelled()){
    log.info("deal async task is Cancelled");
    break;
   }
   if (future.isDone() ) {
    log.info("deal async task is Done");
    log.info("return result is " + future.get());
    break;
   }
   log.info("wait async task to end ...");
   Thread.sleep(1000);
  }
 }

日志打印如下,我们可以看出任务一直在等待异步任务执行完毕,用future.get()来获取异步任务的返回结果

begin to deal other Task!
wait async task to end ...
wait async task to end ...
wait async task to end ...
wait async task to end ...
deal async task is Done
return result is {"thread":"AsyncExecutorThread-1","time":1499752617330}

4.异常处理
我们可以实现AsyncConfigurer接口,也可以继承AsyncConfigurerSupport类来实现

在方法getAsyncExecutor()中创建线程池的时候,必须使用 executor.initialize() ,不然在调用时会报线程池未初始化的异常。

如果使用threadPoolTaskExecutor()来定义bean,则不需要初始化

@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {

// @Bean
// public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
//  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//  executor.setCorePoolSize(10);
//  executor.setMaxPoolSize(100);
//  executor.setQueueCapacity(100);
//  return executor;
// }

 @Override
 public Executor getAsyncExecutor() {
  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  executor.setCorePoolSize(10);
  executor.setMaxPoolSize(100);
  executor.setQueueCapacity(100);
  executor.setThreadNamePrefix("AsyncExecutorThread-");
  executor.initialize(); //如果不初始化,导致找到不到执行器
  return executor;
 }
 @Override
 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  return new AsyncExceptionHandler();
 }
}

异步异常处理类:

@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
 @Override
 public void handleUncaughtException(Throwable ex,Method method,Object... params) {
  log.info("Async method: {} has uncaught exception,params:{}",method.getName(),JSON.toJSONString(params));

  if (ex instanceof AsyncException) {
   AsyncException asyncException = (AsyncException) ex;
   log.info("asyncException:{}",asyncException.getErrorMessage());
  }

  log.info("Exception :");
  ex.printStackTrace();
 }
}

异步处理异常类:

@Data
@AllArgsConstructor
public class AsyncException extends Exception {
 private int code;
 private String errorMessage;
}
  • 在无返回值的异步调用中,异步处理抛出异常,AsyncExceptionHandler的handleUncaughtException()会捕获指定异常,原有任务还会继续运行,直到结束。
  • 在有返回值的异步调用中,异步处理抛出异常,会直接抛出异常,异步任务结束,原有处理结束执行。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对编程小技巧的支持。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


本文从从Bitcask存储模型讲起,谈轻量级KV系统设计与实现。从来没有最好的K-V系统,只有最适合应用业务实际场景的系统,做任何的方案选择,要结合业务当前的实际情况综合权衡,有所取有所舍。
内部的放到gitlab pages的博客,需要统计PV,不蒜子不能准确统计,原因在于gitlab的host设置了strict-origin-when-cross-origin, 导致不蒜子不能正确获取referer,从而PV只能统计到网站的PV。 为了方便统计页面的PV,这里简单的写了一个java程
PCM 自然界中的声音非常复杂,波形极其复杂,通常我们采用的是脉冲代码调制编码,即PCM编码。PCM通过抽样、量化、编码三个步骤将连续变化的模拟信号转换为数字编码。 采样率 采样频率,也称为采样速度或者采样率,定义了每秒从连续信号中提取并组成离散信号的采样个数,它用赫兹(Hz)来表示。采样频率的倒数
本文介绍如何离线生成sst并在线加载,提供一种用rocksdb建立分布式kv系统替换mongodb的思路
验证用户输入是否正确是我们应用程序中的常见功能。Spring提供了`@Valid`和@`Validated`两个注解来实现验证功能,本文详细介绍 [@Valid]和[@Validated]注解的区别 。
引入pdf2dom &lt;dependency&gt; &lt;groupId&gt;net.sf.cssbox&lt;/groupId&gt; &lt;artifactId&gt;pdf2dom&lt;/artifactId&gt; &lt;version&gt;1.8&lt;/version&
grafana 是一款非常优秀的可视化报表工具,有设计精良的可视化工具,今天来聊一聊如何将grafana集成到自己的应用中。 原理是: grafana允许iframe访问,开启auth.proxy, java 后端鉴权后代理grafana 前端通过iframe访问后端代理过的grafana graf
介绍 Call Graph是一款IDEA插件,用于可视化基于IntelliJ平台的IDE的函数调用图。 这个插件的目标是让代码更容易理解,有助于读懂和调试代码。当前只支持Java。针对Typescript、Javascript或Python工具,可以使用作者的另外一款工具Codemap(https:
原理 通过线程安全findAndModify 实现锁 实现 定义锁存储对象: /** * mongodb 分布式锁 */ @Data @NoArgsConstructor @AllArgsConstructor @Document(collection = &quot;distributed-loc
Singleton 单例模式 单例模式是确保每个应用程序只存在一个实例的机制。默认情况下,Spring将所有bean创建为单例。 你用@Autowired获取的bean,全局唯一。 @RestController public class LibraryController { @Autowired
pipeline 分布式任务调度器 目标: 基于docker的布式任务调度器, 比quartzs,xxl-job 更强大的分布式任务调度器。 可以将要执行的任务打包为docker镜像,或者选择已有镜像,自定义脚本程序,通过pipeline框架来实现调度。 开源地址: https://github.c
python训练的模型,转换为onnx模型后,用python代码可以方便进行推理,但是java代码如何实现呢? 首先ONNX 推理,可以使用`onnxruntime` ```xml com.microsoft.onnxruntime onnxruntime 1.15.1 ``` 另外,训练的模型需要
要获取内网地址,可以尝试连接到10.255.255.255:1。如果连接成功,获取本地套接字的地址信息就是当前的内网IP。 python实现: ```python import socket def extract_ip(): st = socket.socket(socket.AF_INET, s
为什么要有索引 gremlin 其实是一个逐级过滤的运行机制,比如下面的一个简单的gremlin查询语句: g.V().hasLabel(&quot;label&quot;).has(&quot;prop&quot;,&quot;value&quot;) 运行原理就是: 找出所有的顶点V 然后过滤出
最近在分析一个应用中的某个接口的耗时情况时,发现一个看起来极其普通的对象创建操作,竟然每次需要消耗 8ms 左右时间,分析后发现这个对象可以通过对象池模式进行优化,优化后此步耗时仅有 0.01ms。
点赞再看,动力无限。Hello world : ) 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码网站 已经收录,有很多知识点和系列文章。 此篇文章介绍 Java JMX 技术的相关概念和具体的使用方式。 当前文章属于Java 性能分析优化系列
如何将Java JAR 转化为 win/mac/linux 独立可执行程序?不需要预装 JRE 运行?
点赞再看,动力无限。 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。 Java 19 在2022 年 9 月 20 日正式发布,Java 19 不是一个长期支持版本,直到 2023 年 3 月它将被 JD
点赞再看,动力无限。Hello world : ) 微信搜「 程序猿阿朗 」。 本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。 前言 Java 反编译,一听可能觉得高深莫测,其实反编译并不是什么特别高级的操作,Java 对于 Cla
JSON 对于开发者并不陌生,如今的 WEB 服务、移动应用、甚至物联网大多都是以 **JSON** 作为数据交换的格式。学习 JSON 格式的操作工具对开发者来说是必不可少的。这篇文章将介绍如何使用 **Jackson** 开源工具库对 JSON 进行常见操作。