工作中經常涉及異步任務,通常是使用多線程技術,比如線程池ThreadPoolExecutor,它的執行規則如下:
在Springboot中對其進行了簡化處理,只需要配置一個類型爲java.util.concurrent.TaskExecutor或其子類的bean,並在配置類或直接在程序入口類上聲明註解@EnableAsync
。
調用也簡單,在由Spring管理的對象的方法上標註註解@Async
,顯式調用即可生效。
一般使用Spring提供的ThreadPoolTaskExecutor類。
聲明
@Configuration
@EnableAsync
public class BeanConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設置核心線程數
executor.setCorePoolSize(5);
// 設置最大線程數
executor.setMaxPoolSize(10);
// 設置隊列容量
executor.setQueueCapacity(20);
// 設置線程活躍時間(秒)
executor.setKeepAliveSeconds(60);
// 設置默認線程名稱
executor.setThreadNamePrefix("hello-");
// 設置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任務結束後再關閉線程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
調用
@Component
public class Hello {
@Async
public void sayHello(String name) {
LoggerFactory.getLogger(Hello.class).info(name + ":Hello World!");
}
}
測試
從打印日誌中可以看出線程池已經正常工作了。
進階
有時候我們不止希望異步執行任務,還希望任務執行完成後會有一個返回值,在java中提供了Future泛型接口,用來接收任務執行結果,springboot也提供了此類支持,使用實現了ListenableFuture接口的類如AsyncResult來作爲返回值的載體。比如上例中,我們希望返回一個類型爲String類型的值,可以將返回值改造爲:
@Async
public ListenableFuture<String> sayHello(String name) {
String res = name + ":Hello World!";
LoggerFactory.getLogger(Hello.class).info(res);
return new AsyncResult<>(res);
}
調用返回值:
@Autowired
private Hello hello;
// 阻塞調用
hello.sayHello("yan").get();
// 限時調用
hello.sayHello("yan").get(1, TimeUnit.SECONDS)
補充
實際上,@Async
還有一個參數,通過Bean名稱來指定調用的線程池-比如上例中設置的線程池參數不滿足業務需求,可以另外定義合適的線程池,調用時指明使用這個線程池-缺省時springboot會優先使用名稱爲'taskExecutor'的線程池,如果沒有找到,纔會使用其他類型爲TaskExecutor或其子類的線程池。