1.介紹
工作中經常涉及異步任務,通常是使用多線程技術,比如線程池ThreadPoolExecutor,但使用Executors容易產生OOM,需要手動使用ThreadPoolExecutor創建線程池;在springboot使用 @async 可以實現異步調用,配置線程池參數,可以簡單的實現多線程的線程池效果,從而簡化開發,避免OOM;
2.異步調用,無返回結果
首先在啟動類上加上@EnableAsync 注解
@SpringBootApplication
@EnableAsync
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class,args);
}
}
在函數上標上@sync注解,表示異步調用
@Async
public void exec2(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("exec2 執行完畢");
}
@GetMapping("exec2")
public String exec2(){
mainService.exec2();
return "exec2";
}
測試:瀏覽器立即返回結果,3秒之后后台才打印輸出日志
2.異步調用,有返回值
@Async
public Future<String> exec3_1(){
try {
Thread.sleep(2500);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("exec3_1 執行完畢");
return new AsyncResult<>("exec3_1");
}
@Async
public Future<String> exec3_2(){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("exec3_2 執行完畢");
return new AsyncResult<>("exec3_2");
}
@GetMapping("exec3")
public String exec3() throws ExecutionException, InterruptedException {
Future<String> exec3_1 = mainService.exec3_1();
Future<String> exec3_2 = mainService.exec3_2();
String result="";
while (true){
if(exec3_1.isDone() && exec3_2.isDone()){
result=exec3_1.get()+"--"+exec3_2.get();
break;
}
}
return result;
}
測試:3秒后瀏覽器返回結果
4.線程池
在異步掉用中使用的@Async 注解,默認的線程池大小如下
# 核心線程數
spring.task.execution.pool.core-size=8
# 最大線程數
spring.task.execution.pool.max-size=16
# 空閑線程存活時間
spring.task.execution.pool.keep-alive=60s
# 是否允許核心線程超時
spring.task.execution.pool.allow-core-thread-timeout=true
# 線程隊列數量
spring.task.execution.pool.queue-capacity=100
# 線程關閉等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 線程名稱前綴
spring.task.execution.thread-name-prefix=task-
一般情況下,我們都需要手動創建線程池,使用 ThreadPoolTaskExecutor 類進行配置
@Configuration
public class PoolConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設置核心線程數
executor.setCorePoolSize(10);
// 設置最大線程數
executor.setMaxPoolSize(15);
// 設置隊列容量
executor.setQueueCapacity(20);
// 設置線程活躍時間(秒)
executor.setKeepAliveSeconds(60);
// 設置默認線程名稱
executor.setThreadNamePrefix("zszxz-");
// 設置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任務結束后再關閉線程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}