目的是:
通過實現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 的方法中 即會捕捉到信息。。。。。。