Spring Boot默認提供了一個ThreadPoolTaskExecutor作為線程調度器,只需要在配置類中使用注解EnableAsync即可開啟異步線程調度。在實際要執行的Bean中使用@Async注解來聲明這個方法是異步方法,需要通過線程調度器來執行。
示例代碼如下:
Application類,開啟異步線程調度
@SpringBootApplication
@EnableAsync
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
異步任務類,聲明execute()方法通過線程調度器執行
@Service
public class TestAsync {
private static AtomicInteger count = new AtomicInteger(0);
@Async
public String execute() {
System.out.println("begin execute TestAsync: " + count.incrementAndGet());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("finished execute TestAsync");
return "result";
}
}
要注意的問題:
(1)這里的@Async注解必須要聲明在Bean對外提供的方法上,如果這個方法不是由其它類直接調用,而是這個類的其它方法間接調用,則不生效。
(2)@Async注解聲明的方法,返回類型要么聲明為void,要么聲明為Future。因為方法是異步調用,因此無法立即返回結果,如果聲明為其它返回類型,則獲取到的是null。聲明為Future,則可以獲取到任務的執行結果。
Controller類
@RestController
public class TestAsyncController {
@Autowired
private TestAsync testAsync;
@RequestMapping(value = "/async", method = RequestMethod.GET)
public String async() {
System.out.println("before execute TestAsync");
String result = testAsync.outexecute();
System.out.println("after execute TestAsync");
return result;
}
}
這里獲取到的result值null,因此獲取這個返回值沒有什么意義。
Spring Boot線程調度有以下幾個參數可以配置(2.1版本之后才有):
spring.task.execution.pool.core-size # 核心線程數,默認為8
spring.task.execution.pool.queue-capacity # 隊列容量,默認為無限大
spring.task.execution.pool.max-size # 最大線程數,默認為無限大
這三個參數的關系如下:
如果當前要執行的任務數超過core-size,則任務會放到隊列里面等待執行,等核心線程中有任務執行完成之后,再取出隊列中的任務進行調度執行。
如果等待隊列已經滿了,再收到新任務時,則核心線程會自動擴容,最大擴展到max-size。
spring.task.execution.pool.allow-core-thread-timeout # 是否允許回收空閑的線程,默認為true
spring.task.execution.pool.keep-alive # 空閑的線程可以保留多少秒,默認為60。如果超過這個時間沒有任務調度,則線程會被回收
spring.task.execution.thread-name-prefix # 線程名前綴,默認為thread-
自定義線程調度器
如果不想使用Spring Boot自帶的線程調度器,可以通過實現AsyncConfigurer接口來定義自己的線程調度器。
示例代碼如下:
@Configuration
@EnableAsync
public class AppConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return MyAsyncUncaughtExceptionHandler();
}
}
