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

所属分类: 软件编程 / java 阅读数: 31
收藏 0 赞 0 分享

本文主要给大家介绍了关于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()会捕获指定异常,原有任务还会继续运行,直到结束。
  • 在有返回值的异步调用中,异步处理抛出异常,会直接抛出异常,异步任务结束,原有处理结束执行。

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

更多精彩内容其他人还在看

Java concurrency之锁_动力节点Java学院整理

这篇文章主要为大家详细介绍了Java concurrency之锁的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java8新特性之StampedLock_动力节点Java学院整理

本文从synchronized、Lock到Java8新增的StampedLock进行对比分析,对Java8新特性之StampedLock相关知识感兴趣的朋友一起看看吧
收藏 0 赞 0 分享

Java8新特性之lambda的作用_动力节点Java学院整理

我们期待了很久lambda为java带来闭包的概念,但是如果我们不在集合中使用它的话,就损失了很大价值。现有接口迁移成为lambda风格的问题已经通过default methods解决了,在这篇文章将深入解析Java集合里面的批量数据操作解开lambda最强作用的神秘面纱。
收藏 0 赞 0 分享

Java8新特性之Base64详解_动力节点Java学院整理

这篇文章主要为大家详细介绍了Java8新特性之Base64的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

Java8新特性之JavaFX 8_动力节点Java学院整理

这篇文章主要介绍了Java8新特性之JavaFX 8的相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧
收藏 0 赞 0 分享

将本地jar包安装进入maven仓库(实现方法)

下面小编就为大家带来一篇将本地jar包安装进入maven仓库(实现方法)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

浅谈Java finally语句到底是在return之前还是之后执行(必看篇)

下面小编就为大家带来一篇浅谈Java finally语句到底是在return之前还是之后执行(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

基于Java并发容器ConcurrentHashMap#put方法解析

下面小编就为大家带来一篇基于Java并发容器ConcurrentHashMap#put方法解析。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

详解Spring Boot Profiles 配置和使用

本篇文章主要介绍了详解Spring Boot Profiles 配置和使用,具有一定的参考价值,有兴趣的可以了解一下
收藏 0 赞 0 分享

详解Spring Boot 属性配置和使用

本篇文章主要介绍了详解Spring Boot 属性配置和使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享
查看更多