如何開(kāi)啟異步調(diào)用
在SpringBoot中,只需要給方法加上@Async注解,就能將同步方法變?yōu)楫惒秸{(diào)用。
首先在啟動(dòng)類上添加@EnableAsync,即開(kāi)啟異步調(diào)用。
- /**
- * @author qcy
- */
- @SpringBootApplication
- @EnableAsync
- public class AsyncApplication {
- public static void main(String[] args) {
- SpringApplication.run(AsyncApplication.class, args);
- }
- }
在需要異步調(diào)用的方法上加上@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開(kāi)始,執(zhí)行線程為" + Thread.currentThread().getName());
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method1結(jié)束");
- }
- @Async
- public void method2() {
- log.info("method2開(kāi)始,執(zhí)行線程為" + Thread.currentThread().getName());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method2結(jié)束");
- }
- }
測(cè)試一下:
- @SpringBootTest
- @Slf4j
- public class AsyncApplicationTests {
- @Autowired
- Task task;
- @Test
- public void testAsyncWithVoidReturn() throws InterruptedException {
- log.info("main線程開(kāi)始");
- task.method1();
- task.method2();
- //確保兩個(gè)異步調(diào)用執(zhí)行完成
- Thread.sleep(6000);
- log.info("main線程結(jié)束");
- }
- }
輸出如下:
可以看得出,SpringBoot創(chuàng)建了一個(gè)名為applicationTaskExecutor的線程池,使用這里面的線程來(lái)執(zhí)行異步調(diào)用。
這里值得注意的是,不要在一個(gè)類中調(diào)用@Async標(biāo)注的方法,否則不會(huì)起到異步調(diào)用的作用,至于為什么會(huì)產(chǎn)生這樣的問(wèn)題,需要深入到源碼中一探究竟,會(huì)另開(kāi)篇幅。
既然默認(rèn)使用的是SpringBoot自己創(chuàng)建的applicationTaskExecutor,那如何自己去定義一個(gè)線程池呢?
自定義線程池
我們需要手動(dòng)創(chuàng)建一個(gè)名為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;
- }
- }
對(duì)以上參數(shù)不了解的同學(xué),可以參考我的這篇文章說(shuō)說(shuō)線程池
其他類不需要變動(dòng),直接運(yùn)行剛才的testAsyncWithVoidReturn()方法,輸出:
看得出來(lái),現(xiàn)在是我們自定義的線程池
如果關(guān)心異步調(diào)用的返回值,又怎么處理?
獲取異步調(diào)用的返回結(jié)果
獲取異步調(diào)用的結(jié)果,需要利用Future機(jī)制,可以參考我的另外一篇文章談?wù)凴unnable、Future、Callable、FutureTask之間的關(guān)系
為Task類增加以下兩個(gè)方法:
- @Async
- public Future<String> method3() {
- log.info("method3開(kāi)始,執(zhí)行線程為" + Thread.currentThread().getName());
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method3結(jié)束");
- return new AsyncResult<>("method3");
- }
- @Async
- public Future<String> method4() {
- log.info("method4開(kāi)始,執(zhí)行線程為" + Thread.currentThread().getName());
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- log.info("method4結(jié)束");
- return new AsyncResult<>("method4");
- }
測(cè)試類:
- @Test
- public void testAsyncWithStringReturn() throws InterruptedException, ExecutionException {
- log.info("main線程開(kāi)始");
- Future<String> method3Result = task.method3();
- Future<String> method4Result = task.method4();
- //get方法為阻塞獲取
- log.info("method3執(zhí)行的返回結(jié)果:{}", method3Result.get());
- log.info("method4執(zhí)行的返回結(jié)果:{}", method4Result.get());
- log.info("main線程結(jié)束");
- }
輸出:
如圖,在主線程結(jié)束前,獲取到了異步調(diào)用的結(jié)果。且在兩個(gè)異步調(diào)用都結(jié)束的情況下,繼續(xù)執(zhí)行主線程。
原文地址:https://www.toutiao.com/i6944877804123079207/