IDC

详解SpringBoot中的异步调用@Async

作者:admin 2021-04-12 我要评论

如何开启异步调用 在SpringBoot中,只需要给方法加上@Async注解,就能将同步方法变为异步调用。 首先在启动类上添加@EnableAsync,即开启异步调用。 /** *@autho...

在说正事之前,我要推荐一个福利:你还在原价购买阿里云、腾讯云、华为云服务器吗?那太亏啦!来这里,新购、升级、续费都打折,能够为您省60%的钱呢!2核4G企业级云服务器低至69元/年,点击进去看看吧>>>)

如何开启异步调用

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

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

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

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

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

测试一下:

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

输出如下:

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

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

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

自定义线程池

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

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

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

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

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

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

获取异步调用的返回结果

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

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

  1. @Async 
  2.   public Future<String> 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.  
  13.   @Async 
  14.   public Future<String> method4() { 
  15.       log.info("method4开始,执行线程为" + Thread.currentThread().getName()); 
  16.       try { 
  17.           Thread.sleep(3000); 
  18.       } catch (InterruptedException e) { 
  19.           e.printStackTrace(); 
  20.       } 
  21.       log.info("method4结束"); 
  22.       return new AsyncResult<>("method4"); 
  23.   } 

测试类:

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

输出:

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


本文转载自网络,原文链接:https://www.toutiao.com/i6944877804123079207/

版权声明:本文转载自网络,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。本站转载出于传播更多优秀技术知识之目的,如有侵权请联系QQ/微信:153890879删除

相关文章
  • 详解SpringBoot中的异步调用@Async

    详解SpringBoot中的异步调用@Async

  • SpringBoot这些常用注解你该知道

    SpringBoot这些常用注解你该知道

  • Java基础入门之Random类和Random方法

    Java基础入门之Random类和Random方法

  • Java编程内功-数据结构与算法「二叉排

    Java编程内功-数据结构与算法「二叉排

腾讯云代理商
海外云服务器