如何开启异步调用
在SpringBoot中,只需要给方法加上@Async注解,就能将同步方法变为异步调用。
首先在启动类上添加@EnableAsync,即开启异步调用。
- /**
- * @author qcy
- */
- @SpringBootApplication
- @EnableAsync
- public class AsyncApplication {
- public static void main(String[] args) {
- SpringApplication.run(AsyncApplication.class, args);
- }
- }
在需要异步调用的方法上加上@Async注解
- package com.yang.async;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.scheduling.annotation.Async;
- import org.springframework.scheduling.annotation.AsyncResult;
- import org.springframework.stereotype.Component;
- import java.util.concurrent.Future;
- import java.util.concurrent.FutureTask;
- /**
- * @author qcy
- * @create 2020/09/09 14:01:35
- */
- @Slf4j
- @Component
- public class Task {
- @Async
- public void method1() {
- log.info("method1开始,执行线程为" + Thread.currentThread().getName());
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method1结束");
- }
- @Async
- public void method2() {
- log.info("method2开始,执行线程为" + Thread.currentThread().getName());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method2结束");
- }
- }
测试一下:
- @SpringBootTest
- @Slf4j
- public class AsyncApplicationTests {
- @Autowired
- Task task;
- @Test
- public void testAsyncWithVoidReturn() throws InterruptedException {
- log.info("main线程开始");
- task.method1();
- task.method2();
- //确保两个异步调用执行完成
- Thread.sleep(6000);
- log.info("main线程结束");
- }
- }
输出如下:
可以看得出,SpringBoot创建了一个名为applicationTaskExecutor的线程池,使用这里面的线程来执行异步调用。
这里值得注意的是,不要在一个类中调用@Async标注的方法,否则不会起到异步调用的作用,至于为什么会产生这样的问题,需要深入到源码中一探究竟,会另开篇幅。
既然默认使用的是SpringBoot自己创建的applicationTaskExecutor,那如何自己去定义一个线程池呢?
自定义线程池
我们需要手动创建一个名为asynTaskExecutord的Bean
- package com.yang.async;
- import lombok.extern.slf4j.Slf4j;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.task.AsyncTaskExecutor;
- import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
- import java.util.concurrent.ThreadPoolExecutor;
- /**
- * @author qcy
- * @create 2020/09/09 15:31:07
- */
- @Slf4j
- @Configuration
- public class AsyncConfig {
- @Bean
- public AsyncTaskExecutor asyncTaskExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(8);
- executor.setMaxPoolSize(16);
- executor.setQueueCapacity(50);
- executor.setAllowCoreThreadTimeOut(true);
- executor.setKeepAliveSeconds(10);
- executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
- executor.setThreadNamePrefix("async-thread-pool-thread");
- return executor;
- }
- }
对以上参数不了解的同学,可以参考我的这篇文章说说线程池
其他类不需要变动,直接运行刚才的testAsyncWithVoidReturn()方法,输出:
看得出来,现在是我们自定义的线程池
如果关心异步调用的返回值,又怎么处理?
获取异步调用的返回结果
获取异步调用的结果,需要利用Future机制,可以参考我的另外一篇文章谈谈Runnable、Future、Callable、FutureTask之间的关系
为Task类增加以下两个方法:
- @Async
- public Future<String> method3() {
- log.info("method3开始,执行线程为" + Thread.currentThread().getName());
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method3结束");
- return new AsyncResult<>("method3");
- }
- @Async
- public Future<String> method4() {
- log.info("method4开始,执行线程为" + Thread.currentThread().getName());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method4结束");
- return new AsyncResult<>("method4");
- }
测试类:
- @Test
- public void testAsyncWithStringReturn() throws InterruptedException, ExecutionException {
- log.info("main线程开始");
- Future<String> method3Result = task.method3();
- Future<String> method4Result = task.method4();
- //get方法为阻塞获取
- log.info("method3执行的返回结果:{}", method3Result.get());
- log.info("method4执行的返回结果:{}", method4Result.get());
- log.info("main线程结束");
- }
输出:
如图,在主线程结束前,获取到了异步调用的结果。且在两个异步调用都结束的情况下,继续执行主线程。
原文地址:https://www.toutiao.com/i6944877804123079207/