详解SpringBoot中的异步调用@Async

如何开启异步调用

在SpringBoot中,只需要给方法加上@Async注解,就能将同步方法变为异步调用。

首先在启动类上添加@EnableAsync,即开启异步调用。

 
 
 
 
  1. /**
  2.  * @author qcy
  3.  */
  4. @SpringBootApplication
  5. @EnableAsync
  6. public class AsyncApplication {
  7.     public static void main(String[] args) {
  8.         SpringApplication.run(AsyncApplication.class, args);
  9.     }
  10. }

在需要异步调用的方法上加上@Async注解

 
 
 
 
  1. package com.yang.async;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.scheduling.annotation.Async;
  4. import org.springframework.scheduling.annotation.AsyncResult;
  5. import org.springframework.stereotype.Component;
  6. import java.util.concurrent.Future;
  7. import java.util.concurrent.FutureTask;
  8. /**
  9.  * @author qcy
  10.  * @create 2020/09/09 14:01:35
  11.  */
  12. @Slf4j
  13. @Component
  14. public class Task {
  15.     @Async
  16.     public void method1() {
  17.         log.info("method1开始,执行线程为" + Thread.currentThread().getName());
  18.         try {
  19.             Thread.sleep(2000);
  20.         } catch (InterruptedException e) {
  21.             e.printStackTrace();
  22.         }
  23.         log.info("method1结束");
  24.     }
  25.     @Async
  26.     public void method2() {
  27.         log.info("method2开始,执行线程为" + Thread.currentThread().getName());
  28.         try {
  29.             Thread.sleep(3000);
  30.         } catch (InterruptedException e) {
  31.             e.printStackTrace();
  32.         }
  33.         log.info("method2结束");
  34.     }
  35. }

 测试一下:

 
 
 
 
  1. @SpringBootTest
  2. @Slf4j
  3. public class AsyncApplicationTests {
  4.     @Autowired
  5.     Task task;
  6.     @Test
  7.     public void testAsyncWithVoidReturn() throws InterruptedException {
  8.         log.info("main线程开始");
  9.         task.method1();
  10.         task.method2();
  11.         //确保两个异步调用执行完成
  12.         Thread.sleep(6000);
  13.         log.info("main线程结束");
  14.     }
  15. }

 输出如下:

可以看得出,SpringBoot创建了一个名为applicationTaskExecutor的线程池,使用这里面的线程来执行异步调用。

这里值得注意的是,不要在一个类中调用@Async标注的方法,否则不会起到异步调用的作用,至于为什么会产生这样的问题,需要深入到源码中一探究竟,会另开篇幅。

既然默认使用的是SpringBoot自己创建的applicationTaskExecutor,那如何自己去定义一个线程池呢?

自定义线程池

我们需要手动创建一个名为asynTaskExecutord的Bean

 
 
 
 
  1. package com.yang.async;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.core.task.AsyncTaskExecutor;
  6. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  7. import java.util.concurrent.ThreadPoolExecutor;
  8. /**
  9.  * @author qcy
  10.  * @create 2020/09/09 15:31:07
  11.  */
  12. @Slf4j
  13. @Configuration
  14. public class AsyncConfig {
  15.     @Bean
  16.     public AsyncTaskExecutor asyncTaskExecutor() {
  17.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  18.         executor.setCorePoolSize(8);
  19.         executor.setMaxPoolSize(16);
  20.         executor.setQueueCapacity(50);
  21.         executor.setAllowCoreThreadTimeOut(true);
  22.         executor.setKeepAliveSeconds(10);
  23.         executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
  24.         executor.setThreadNamePrefix("async-thread-pool-thread");
  25.         return executor;
  26.     }
  27. }

 对以上参数不了解的同学,可以参考我的这篇文章说说线程池

其他类不需要变动,直接运行刚才的testAsyncWithVoidReturn()方法,输出:

看得出来,现在是我们自定义的线程池

如果关心异步调用的返回值,又怎么处理?

获取异步调用的返回结果

获取异步调用的结果,需要利用Future机制,可以参考我的另外一篇文章谈谈Runnable、Future、Callable、FutureTask之间的关系

为Task类增加以下两个方法:

 
 
 
 
  1. @Async
  2.   public Future method3() {
  3.       log.info("method3开始,执行线程为" + Thread.currentThread().getName());
  4.       try {
  5.           Thread.sleep(1000);
  6.       } catch (InterruptedException e) {
  7.           e.printStackTrace();
  8.       }
  9.       log.info("method3结束");
  10.       return new AsyncResult<>("method3");
  11.   }
  12.   @Async
  13.   public Future method4() {
  14.       log.info("method4开始,执行线程为" + Thread.currentThread().getName());
  15.       try {
  16.           Thread.sleep(3000);
  17.       } catch (InterruptedException e) {
  18.           e.printStackTrace();
  19.       }
  20.       log.info("method4结束");
  21.       return new AsyncResult<>("method4");
  22.   }

 测试类:

 
 
 
 
  1. @Test
  2.   public void testAsyncWithStringReturn() throws InterruptedException, ExecutionException {
  3.       log.info("main线程开始");
  4.       Future method3Result = task.method3();
  5.       Future method4Result = task.method4();
  6.       //get方法为阻塞获取
  7.       log.info("method3执行的返回结果:{}", method3Result.get());
  8.       log.info("method4执行的返回结果:{}", method4Result.get());
  9.       log.info("main线程结束");
  10.   }

 输出:

如图,在主线程结束前,获取到了异步调用的结果。且在两个异步调用都结束的情况下,继续执行主线程。

分享标题:详解SpringBoot中的异步调用@Async
地址分享:http://www.csdahua.cn/qtweb/news26/430826.html

网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网