JDK執行緒池的拒絕策略
關於新疆服務請求未帶入來話原因的問題
經核查,該問題是由於立單介面內部沒有成功呼叫接續的 “更新來電原因介面”導致的,接續測更新來電原因介面編碼:NGCCT_UPDATESRFLAG_PUT ,立單介面呼叫程式碼如下:
final Map<String, Object> paramsMap = outputObject.getBean(); paramsMap.put("provCode", provCode); paramsMap.put("tenantId", MapUtils.getString(inputObject.getParams(), "tenantId")); TaskEngine.getInstance().submit(new Runnable() { @Override public void run() { callContactRel(paramsMap);[zhai1] backWriteWrkfm(paramsMap); } });
立單介面中使用起多執行緒非同步呼叫方式,更新來電原因方法內部記錄日誌,由於執行緒內部日誌無法在火眼系統檢視,當出現該問題時,
1.多次找線上運維人員檢視主機上的日誌,均未發現更新來話原因方法內部記錄的日誌;
2.登陸csf平臺查詢該介面的呼叫記錄,根據出現問題的流水號均未查到相關呼叫記錄;
3.核查呼叫更新來電原因方法內部catch中如表的錯誤記錄,也未發現問題;
根據火眼上上下文日誌,確定程式一定執行到了啟動多執行緒的地方,遂懷疑執行緒池的問題,
該處啟用多執行緒使用的是 TaskEngine工具提供的獲取執行緒池,使用池中資源執行任務。
經核查TaskEngine類發現該類在初始化的時候會建立一個執行緒池,核心執行緒數為1,最大執行緒數為30的執行緒池,程式碼如下:
private static TaskEngine instance = new TaskEngine(); public static TaskEngine getInstance() { return instance; } private TaskEngine() { executor= new ThreadPoolExecutor(1, 30,180L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactory() { final AtomicInteger threadNumber = new AtomicInteger(1); public Thread newThread(Runnable runnable) { // Use our own naming scheme for the threads. Thread thread = new Thread(Thread.currentThread().getThreadGroup(), runnable, "TaskEngine-pool-" + threadNumber.getAndIncrement(), 0); // Make workers daemon threads. thread.setDaemon(true); if (thread.getPriority() != Thread.NORM_PRIORITY) { thread.setPriority(Thread.NORM_PRIORITY); } return thread; } }); }
該執行緒池構造時尚未指明 拒絕策略 [zhai2] 因此會預設使用 AbortPolicy
JDK提供的ThreadPoolExecutor 執行緒池有四種 拒絕策略:
AbortPolicy 當任務新增到執行緒池中被拒絕時,它將丟擲 RejectedExecutionException 異常。
CallerRunsPolicy 當任務新增到執行緒池中被拒絕時,會線上程池當前正在執行的Thread執行緒池中處理被拒絕的任務。
DiscardOldestPolicy 當任務新增到執行緒池中被拒絕時,執行緒池會放棄等待佇列中最舊的未處理任務,然後將被拒絕的任務新增到等待佇列中。
DiscardPolicy 當任務新增到執行緒池中被拒絕時,執行緒池將丟棄被拒絕的任務。
工程中使用的預設的策略,會不會存在一種情況,在某一個時刻,使用TaskEngine類獲取執行緒池來執行任務時,30個執行緒同時被使用,導致建立任務數大於最大執行緒數的限制,該任務將無法被成功執行。
Demo驗證:
1.使用與專案中相同的執行緒池執行資料入庫操作,模擬設定最大執行緒數10,核心執行緒1個,拒絕策略預設:
@RequestMapping(value = "/testThread") public String testThread(){ final CommonCfgCode commonCfgCode = new CommonCfgCode(); commonCfgCode.setCodeTypeCd("Thread"); TaskEngine taskEngine = TaskEngine.getInstance(); for(int i=0;i<100;i++){ taskEngine.submit(new Runnable() { @Override public void run() { testService.save(commonCfgCode); } }); } return "SUCCESS"; }
迴圈執行100個任務進行如表操作丟擲:
java.util.concurrent.RejectedExecutionException異常,與該策略描述一致。
檢視如表資料:僅如表10條資料;
2.將拒絕策略修改為:CallerRunsPolicy 其它不變;
無報錯,查詢資料:如表100條;(110條中包含使用預設拒絕拋異常策略如表的10條)
Demo git路徑:https://git.lug.ustc.edu.cn/zhaiyt/mylife.git
由於該問題僅在新疆出現,出現的場景無法預知,請各位評審是否可以修改專案中TaskEngine類。
[zhai1]呼叫接續介面更新來話原因方法
[zhai2]執行緒池的拒絕策略,是指當任務新增到執行緒池中被拒絕,而採取的處理措施。當任務新增到執行緒池中之所以被拒絕,可能是由於:第一,執行緒池異常關閉。第二,任務數量超過執行緒池的最大限制。