@Async一个注解搞定异步编程

我不是女神ヾ 2024-03-24 11:28 95阅读 0赞

前言

今天我们来说一下另外一个基于注解的异步编程利器@Async,使用它代码会更加的简洁,更加的规范,不过在使用它的时候也会配合Future接口,下面我们会详细的介绍!

@Async解析

@Async可以使用在方法上面,也可以使用在类上面,如果在类上使用,那么整个类的所有方法都是异步的,@Async注解的value是设置线程池,如果不设置,那么就会使用默认的SimpleAsyncTaskExecutor线程池,不过在实际使用中,我们肯定不能使用默认的,应该自定义一个线程池。

  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface Async {
  5. String value() default "";
  6. }
  7. 复制代码

@EnableAsync解析

在SpringBoot中如果要使用@Async,那么需要在项目的启动类上加上@EnableAsync注解,加上@EnableAsync注解,在启动SpringBoot项目的时候它会做一些配置,因为@Async本身就是基于AOP来实现,所以在项目启动的时候会去读取注解的信息,然后做相应的配置,会在Spring Bean进行装配的时候配置,@Async就会在Bean的后置处理BeanPostProcessor做一些配置。

实战演练

一、加上@EnableAsync注解

  1. @EnableAsync
  2. @SpringBootApplication
  3. public class SpringAsyncApplication {
  4. public static void main(String[] args) {
  5. SpringApplication.run(SpringAsyncApplication.class, args);
  6. }
  7. }
  8. 复制代码

配置线程池

在实际使用中,我们需要配置线程池,下面的线程池参数已经写死在代码里面,但是生产环境上,我们应该将其配置在统一配置中心中,如nacos,这样,我们就能根据实际情况对参数进行调整。

  1. /**
  2. * 功能说明:Async线程池配置
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2022-06-26 11:37
  5. * <p>
  6. * Copyright (C)2020-2022 小四的技术之旅 All rights reserved.
  7. */
  8. @Configuration
  9. public class ThreadPoolConfiguration {
  10. @Bean
  11. public TaskExecutor taskExecutor(){
  12. /**
  13. * CPU核数
  14. */
  15. int processors = Runtime.getRuntime().availableProcessors();
  16. ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
  17. threadPoolTaskExecutor.setCorePoolSize(processors * 2);
  18. threadPoolTaskExecutor.setMaxPoolSize(processors * 4);
  19. threadPoolTaskExecutor.setThreadNamePrefix("async-thread-");
  20. threadPoolTaskExecutor.setKeepAliveSeconds(60);
  21. return threadPoolTaskExecutor;
  22. }
  23. }
  24. 复制代码

Service

AsyncService包含了四个异步任务,task1,task2,task3,task4,值得注意的是,使用@Async的方法,其返回值要么为void, 要么为Future,如果为其他类型,那么返回的为空,因为任务是交给线程池,所以需要用Future来记录执行结果,下面使用了CompletableFuture

  1. /**
  2. * 功能说明:
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2022-06-26 11:17
  5. * <p>
  6. * Copyright (C)2020-2022 小四的技术之旅 All rights reserved.
  7. */
  8. @Service
  9. @Slf4j
  10. public class AsyncService {
  11. @Async("taskExecutor")
  12. public void task1() throws InterruptedException {
  13. Thread.sleep(2000);
  14. log.info("=====开始执行task1====={}", Thread.currentThread().getName());
  15. }
  16. @Async("taskExecutor")
  17. public CompletableFuture<String> task2() {
  18. log.info("=====开始执行task2====={}", Thread.currentThread().getName());
  19. return CompletableFuture.completedFuture("task2");
  20. }
  21. @Async("taskExecutor")
  22. public void task3() {
  23. log.info("=====开始执行task3====={}", Thread.currentThread().getName());
  24. }
  25. @Async("taskExecutor")
  26. public void task4() {
  27. log.info("=====开始执行task4====={}", Thread.currentThread().getName());
  28. }
  29. }
  30. 复制代码

Controller

  1. /**
  2. * 功能说明:
  3. * <p>
  4. * Original @Author: steakliu-刘牌, 2022-06-26 11:25
  5. * <p>
  6. * Copyright (C)2020-2022 小四的技术之旅 All rights reserved.
  7. */
  8. @RestController
  9. @AllArgsConstructor
  10. @Slf4j
  11. public class AsyncController {
  12. final AsyncService asyncService;
  13. @GetMapping("/async")
  14. public String async() throws InterruptedException, ExecutionException {
  15. asyncService.task1();
  16. CompletableFuture<String> future = asyncService.task2();
  17. log.info("===task2 result=== {}",future.get());
  18. asyncService.task3();
  19. asyncService.task4();
  20. return "async";
  21. }
  22. }
  23. 复制代码

输出

从输出结果看出几个接口是异步执行的,并且task2返回了值,如果不使用Future作为方法的返回类型,那么将会返回null。

format_png

使用其他返回类型

  1. public String task2(){
  2. log.info("=====开始执行task2====={}",Thread.currentThread().getName());
  3. return "task2";
  4. }
  5. 复制代码

format_png 1

总结

从上面我们可以看出使用@Async还是比较简单的,特别对于SpringBoot,只需要简单的注解就能完事,不过在使用的时候我们要根据实际情况去考虑该 怎么用,比如数据之间的依赖,返回类型等等。

发表评论

表情:
评论列表 (有 0 条评论,95人围观)

还没有评论,来说两句吧...

相关阅读