工作中經(jīng)常涉及異步任務(wù)封断,通常是使用多線程技術(shù),比如線程池ThreadPoolExecutor憨愉,它的執(zhí)行規(guī)則如下:
在Springboot中對其進(jìn)行了簡化處理,只需要配置一個類型為java.util.concurrent.TaskExecutor或其子類的bean卿捎,并在配置類或直接在程序入口類上聲明注解@EnableAsync
配紫。
調(diào)用也簡單,在由Spring管理的對象的方法上標(biāo)注注解@Async
午阵,顯式調(diào)用即可生效躺孝。
一般使用Spring提供的ThreadPoolTaskExecutor類。
聲明
@Configuration
@EnableAsync
public class BeanConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設(shè)置核心線程數(shù)
executor.setCorePoolSize(5);
// 設(shè)置最大線程數(shù)
executor.setMaxPoolSize(10);
// 設(shè)置隊(duì)列容量
executor.setQueueCapacity(20);
// 設(shè)置線程活躍時間(秒)
executor.setKeepAliveSeconds(60);
// 設(shè)置默認(rèn)線程名稱
executor.setThreadNamePrefix("hello-");
// 設(shè)置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任務(wù)結(jié)束后再關(guān)閉線程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
調(diào)用
@Component
public class Hello {
@Async
public void sayHello(String name) {
LoggerFactory.getLogger(Hello.class).info(name + ":Hello World!");
}
}
測試
從打印日志中可以看出線程池已經(jīng)正常工作了底桂。
進(jìn)階
有時候我們不止希望異步執(zhí)行任務(wù)植袍,還希望任務(wù)執(zhí)行完成后會有一個返回值,在java中提供了Future泛型接口籽懦,用來接收任務(wù)執(zhí)行結(jié)果于个,springboot也提供了此類支持,使用實(shí)現(xiàn)了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);
}
調(diào)用返回值:
@Autowired
private Hello hello;
// 阻塞調(diào)用
hello.sayHello("yan").get();
// 限時調(diào)用
hello.sayHello("yan").get(1, TimeUnit.SECONDS)
補(bǔ)充
- 實(shí)際上捶码,
@Async
還有一個參數(shù)羽氮,通過Bean名稱來指定調(diào)用的線程池-比如上例中設(shè)置的線程池參數(shù)不滿足業(yè)務(wù)需求,可以另外定義合適的線程池惫恼,調(diào)用時指明使用這個線程池-缺省時springboot會優(yōu)先使用名稱為'taskExecutor'的線程池档押,如果沒有找到,才會使用其他類型為TaskExecutor或其子類的線程池祈纯。