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

前言

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

@Async解析

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

123456
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Async { String value() default ""; }

@EnableAsync解析

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

实战演练

一、加上@EnableAsync注解

123456789
@EnableAsync @SpringBootApplication public class SpringAsyncApplication { public static void main(String[] args) { SpringApplication.run(SpringAsyncApplication.class, args); } }

配置线程池

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

123456789101112131415161718192021222324
/** * 功能说明:Async线程池配置 * <p> * Original @Author: steakliu-刘牌, 2022-06-26 11:37 * <p> * Copyright (C)2020-2022 小四的技术之旅 All rights reserved. */ @Configuration public class ThreadPoolConfiguration { @Bean public TaskExecutor taskExecutor(){ /** * CPU核数 */ int processors = Runtime.getRuntime().availableProcessors(); ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(processors * 2); threadPoolTaskExecutor.setMaxPoolSize(processors * 4); threadPoolTaskExecutor.setThreadNamePrefix("async-thread-"); threadPoolTaskExecutor.setKeepAliveSeconds(60); return threadPoolTaskExecutor; } }

Service

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

123456789101112131415161718192021222324252627282930313233
/** * 功能说明: * <p> * Original @Author: steakliu-刘牌, 2022-06-26 11:17 * <p> * Copyright (C)2020-2022 小四的技术之旅 All rights reserved. */ @Service @Slf4j public class AsyncService { @Async("taskExecutor") public void task1() throws InterruptedException { Thread.sleep(2000); log.info("=====开始执行task1====={}", Thread.currentThread().getName()); } @Async("taskExecutor") public CompletableFuture<String> task2() { log.info("=====开始执行task2====={}", Thread.currentThread().getName()); return CompletableFuture.completedFuture("task2"); } @Async("taskExecutor") public void task3() { log.info("=====开始执行task3====={}", Thread.currentThread().getName()); } @Async("taskExecutor") public void task4() { log.info("=====开始执行task4====={}", Thread.currentThread().getName()); } }

Controller

123456789101112131415161718192021222324
/** * 功能说明: * <p> * Original @Author: steakliu-刘牌, 2022-06-26 11:25 * <p> * Copyright (C)2020-2022 小四的技术之旅 All rights reserved. */ @RestController @AllArgsConstructor @Slf4j public class AsyncController { final AsyncService asyncService; @GetMapping("/async") public String async() throws InterruptedException, ExecutionException { asyncService.task1(); CompletableFuture<String> future = asyncService.task2(); log.info("===task2 result=== {}",future.get()); asyncService.task3(); asyncService.task4(); return "async"; } }

输出

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

使用其他返回类型

1234
public String task2(){ log.info("=====开始执行task2====={}",Thread.currentThread().getName()); return "task2"; }

总结

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

今天的分享就到这里,感谢你的观看,我们下期见!

乡愁
分布式文件系统Minio
评论
steakliu刘牌公众号【刘牌】成都
文章28
分类14
标签4