Spring Boot默認提供了一個ThreadPoolTaskExecutor作爲線程調度器,只需要在配置類中使用註解EnableAsync即可開啓異步線程調度。在實際要執行的Bean中使用@Async註解來聲明這個方法是異步方法,需要通過線程調度器來執行。
示例代碼如下:
Application類,開啓異步線程調度
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(); } }