目的是:
通過實現AsyncConfigurer自定義線程池,包含異常處理
實現AsyncConfigurer接口對異常線程池更加細粒度的控制
*a) 創建線程自己的線程池
b) 對void方法拋出的異常處理的類AsyncUncaughtExceptionHandler
個人初步理解
一、線程池是為突然大量爆發的線程設計的,通過有限的幾個固定線程為大量的操作服務,減少了創建和銷毀線程所需的時間,
從而提高效率。如果一個線程的時間非常長,就沒必要用線程池了(不是不能作長時間操作,而是不宜。),
況且還不能控制線程池中線程的開始、掛起、和中止。
二、利用線程池啟動線程
Thread udpThread = new Thread(udp);
poolTaskExecutor.execute(udpThread);
獲取當前線程池活動的線程數:
int count = poolTaskExecutor.getActiveCount();
logger.debug("[x] - now threadpool active threads totalNum : " +count);
三、配置解釋
當一個任務通過execute(Runnable)方法欲添加到線程池時:
1、 如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閑狀態,也要創建新的線程來處理被添加的任務。
2、 如果此時線程池中的數量等於 corePoolSize,但是緩沖隊列 workQueue未滿,那么任務被放入緩沖隊列。
3、如果此時線程池中的數量大於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
4、 如果此時線程池中的數量大於corePoolSize,緩沖隊列workQueue滿,並且線程池中的數量等於maximumPoolSize,那么通過 handler所指定的策略來處理此任務。
也就是:處理任務的優先級為:核心線程corePoolSize、任務隊列workQueue、最大線程 maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。
5、 當線程池中的線程數量大於 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態的調整池中的線程數。
allowCoreThreadTimeout:允許核心線程超時
rejectedExecutionHandler:任務拒絕處理器
兩種情況會拒絕處理任務:
當線程數已經達到maxPoolSize,切隊列已滿,會拒絕新任務
當線程池被調用shutdown()后,會等待線程池里的任務執行完畢,再shutdown。如果在調用shutdown()和線程池真正shutdown之間提交任務,會拒絕新任務
線程池會調用rejectedExecutionHandler來處理這個任務。如果沒有設置默認是AbortPolicy,會拋出異常
ThreadPoolExecutor類有幾個內部實現類來處理這類情況:
AbortPolicy 丟棄任務,拋運行時異常
CallerRunsPolicy 執行任務
DiscardPolicy 忽視,什么都不會發生
DiscardOldestPolicy 從隊列中踢出最先進入隊列(最后一個執行)的任務
實現RejectedExecutionHandler接口,可自定義處理器
廢話不多說 上代碼 環境:IDEA +SpringBoot+maven
一、線程池配置類實現AsyncConfigurer 接口:
@Component
public class MyAsyncConfigurer implements AsyncConfigurer {
private static final Logger log = LoggerFactory.getLogger(MyAsyncConfigurer.class);
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();
threadPool.setCorePoolSize(2);//當前線程數
threadPool.setMaxPoolSize(120);// 最大線程數
threadPool.setQueueCapacity(1);//線程池所使用的緩沖隊列
threadPool.setWaitForTasksToCompleteOnShutdown(true);//等待任務在關機時完成--表明等待所有線程執行完
threadPool.setAwaitTerminationSeconds(60 * 15);// 等待時間 (默認為0,此時立即停止),並沒等待xx秒后強制停止
threadPool.setThreadNamePrefix("MyAsync-");// 線程名稱前綴
threadPool.initialize(); // 初始化
System.out.println("--------------------------》》》開啟異常線程池");
return threadPool;
}
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncExceptionHandler();
}
/**
* 自定義異常處理類
* @author hry
*
*/
class MyAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
//手動處理捕獲的異常
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... obj) {
System.out.println("-------------》》》捕獲線程異常信息");
log.info("Exception message - " + throwable.getMessage());
log.info("Method name - " + method.getName());
for (Object param : obj) {
log.info("Parameter value - " + param);
}
}
}
二、使用簡單無參數異步線程進行測試
@Override
@Async
public String asyncMethodWithVoidReturnType() {
System.out.println("線程名稱:"+Thread.currentThread().getName() + " be ready to read data!");
try {
Thread.sleep(1000 * 5);
System.out.println("---------------------》》》無返回值延遲3秒:");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "已進入到異步";
}
三、0--3秒內連續訪問如下結果
線程名稱:MyAsync-1 be ready to read data! 線程名稱:MyAsync-2 be ready to read data! 線程名稱:MyAsync-3 be ready to read data! 線程名稱:MyAsync-4 be ready to read data! 線程名稱:MyAsync-5 be ready to read data! 線程名稱:MyAsync-6 be ready to read data! 線程名稱:MyAsync-7 be ready to read data! 線程名稱:MyAsync-8 be ready to read data! MyAsync-1---------------------》》》無返回值延遲5秒: 線程名稱:MyAsync-1 be ready to read data! MyAsync-2---------------------》》》無返回值延遲5秒: 線程名稱:MyAsync-2 be ready to read data! MyAsync-3---------------------》》》無返回值延遲5秒: MyAsync-4---------------------》》》無返回值延遲5秒: MyAsync-5---------------------》》》無返回值延遲5秒: MyAsync-6---------------------》》》無返回值延遲5秒: MyAsync-7---------------------》》》無返回值延遲5秒: MyAsync-8---------------------》》》無返回值延遲5秒: MyAsync-1---------------------》》》無返回值延遲5秒: MyAsync-2---------------------》》》無返回值延遲5秒:
如上可以看出 線程池發揮作用 多個線程訪問如果超過核心線程數+隊列數 變新創建線程,如果有線程
空閑下來會繼續分配,以此來提高效率。我是利用postMain 來進行的測試,以驗證上面所說的理論問題。。
實際應用中比這個復雜得多。。。。。。。。
四、如果想要手動捕獲異常信息 如下代碼 即可
throw new IllegalArgumentException(s);
將上面代碼 放在需要捕獲信息的中
在線程池配置中 MyAsyncExceptionHandler 的方法中 即會捕捉到信息。。。。。。

