前提摘要:
基於Springboot 2.1.4.RELEASE
▎ 配置TaskExecutor
import java.util.concurrent.ThreadPoolExecutor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設置核心線程數
executor.setCorePoolSize(5);
// 設置最大線程數
executor.setMaxPoolSize(10);
// 設置隊列容量
executor.setQueueCapacity(5);
// 設置線程活躍時間,單位秒
executor.setKeepAliveSeconds(60);
// 設置核心線程超時回收
executor.setAllowCoreThreadTimeOut(true);
// 設置默認線程名稱
executor.setThreadNamePrefix("IThread-");
// 設置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
return executor;
}
}
▎ 參數解讀
● CorePoolSize
核心線程數,核心線程會一直存活,即使沒有任務需要處理。當線程數小於核心線程數時,即使現有的線程空閒,線程池也會優先創建新線程來處理任務,而不是直接交給現有的線程處理。
核心線程在allowCoreThreadTimeout被設置爲true時會超時退出,默認情況下不會退出。
● MaxPoolSize
當線程數大於或等於核心線程,且任務隊列已滿時,線程池會創建新的線程,直到線程數量達到maxPoolSize。如果線程數已等於maxPoolSize,且任務隊列已滿,則已超出線程池的處理能力,線程池會拒絕處理任務而拋出異常。
● queueCapacity
任務隊列容量。從maxPoolSize的描述上可以看出,任務隊列的容量會影響到線程的變化,因此任務隊列的長度也需要恰當的設置。
● keepAliveTime
當線程空閒時間達到keepAliveTime,該線程會退出,直到線程數量等於corePoolSize。如果allowCoreThreadTimeout設置爲true,則所有線程均會退出直到線程數量爲0。
● allowCoreThreadTimeout
是否允許核心線程空閒退出,默認值爲false。
● RejectedExecutionHandler
拒絕策略:當線程數大於MaxPoolSize+queueCapacity被觸發:
☞ CallerRunsPolicy - 當觸發拒絕策略,只要線程池沒有關閉的話,則使用調用線程直接運行任務。一般併發比較小,性能要求不高,不允許失敗。但是,由於調用者自己運行任務,如果任務提交速度過快,可能導致程序阻塞,性能效率上必然的損失較大
☞ AbortPolicy - 丟棄任務,並拋出拒絕執行 RejectedExecutionException 異常信息。線程池默認的拒絕策略。必須處理好拋出的異常,否則會打斷當前的執行流程,影響後續的任務執行。
☞ DiscardPolicy - 直接丟棄,其他啥都沒有
☞ DiscardOldestPolicy - 當觸發拒絕策略,只要線程池沒有關閉的話,丟棄阻塞隊列 workQueue 中最老的一個任務,並將新任務加入
▎ 線程池執行流程圖 圖片來源:https://www.cnblogs.com/yw0219/p/8810956.html
▎ 案例: 使用AbortPolicy拒絕策略,模擬高併發觸發異常
☞ TaskExecutor配置
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 設置核心線程數
executor.setCorePoolSize(5);
// 設置最大線程數
executor.setMaxPoolSize(10);
// 設置隊列容量
executor.setQueueCapacity(5);
// 設置線程活躍時間,單位秒
executor.setKeepAliveSeconds(60);
// 設置核心線程超時回收
executor.setAllowCoreThreadTimeOut(true);
// 設置默認線程名稱
executor.setThreadNamePrefix("IThread-");
// 設置拒絕策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
return executor;
}
☞ 創建異步任務
ExecutorService
public interface ExecutorService {
public void exec();
}
ExecutorServiceImpl
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.mote.service.ExecutorService;
@Service("executorService")
public class ExecutorServiceImpl implements ExecutorService {
private Logger log = LoggerFactory.getLogger(getClass());
@Override
@Async
public void exec() {
log.info(Thread.currentThread().getName() + "開始執行");
try {
// 模擬業務處理耗時
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("<<<<<<<<<<<<<<<<線程執行完畢>>>>>>>>>>>>>>>>");
}
}
☞ 編寫Controller,調用異步任務
ExecutorController
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.mote.service.ExecutorService;
@RestController
public class ExecutorController {
@Autowired
private ExecutorService executorService;
@GetMapping("/executor")
public String executor() {
try {
executorService.exec();
return "success";
} catch (Exception e) {
e.printStackTrace();
}
return "error";
}
}
☞ 打開瀏覽器,訪問Controller接口,不斷刷新模擬高併發,觀察返回結果,如果出現error說明策略被觸發了