前言
之前写了几篇关于异步编程的文章,Future
,FutureTask
,CompletableFuture
,今天我们来说一下另外一个基于注解的异步编程利器@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。
使用其他返回类型
1234public String task2(){
log.info("=====开始执行task2====={}",Thread.currentThread().getName());
return "task2";
}
总结
从上面我们可以看出使用@Async还是比较简单的,特别对于SpringBoot,只需要简单的注解就能完事,不过在使用的时候我们要根据实际情况去考虑该
怎么用,比如数据之间的依赖,返回类型等等。
今天的分享就到这里,感谢你的观看,我们下期见!
一个优雅的写作平台