org.springframework.core.task.TaskRejectedException:Executor[]did not accept task異常解決
今天有個需求是優化查詢時的效率(使用的hbase無法做關聯查詢,需要在結果查出來之後組合),結果就遇到了如題的異常。
那麼通過查詢原始碼發現問題,上程式碼
org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor.java
@Override
public void execute(Runnable task) {
Executor executor = getThreadPoolExecutor();
try {
executor.execute(task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
}
}``
因為在之前沒有遇到過RejectedExecutionException 這個異常,那麼去百度看到了很詳細的解釋,下面是網友的解決方案
Spring ThreadPoolTaskExecutor沒有使用阻塞模式將任務加入到物件中,因此物件滿的時候會丟擲異常,對於這種情況,一般的企業執行環境不能無限制的增大記憶體佇列容量,因此不得不阻塞佇列的加入,spring內建提供的異常處理機制不好用,因為ThreadPoolExecutor.CallerRunsPolicy的處理方式是將異常任務放在呼叫執行緒中執行,這樣對於單個執行時間長的任務,即使佇列有空閒了,剩下的任務也要等這個任務在主執行緒執行完了才能繼續往佇列裡面新增。有一個處理方法就是捕獲executor.execute()的異常,只要發現有異常就等待一段時間,直到沒有異常為止,這樣就能模仿阻塞佇列的效果,下面是程式碼:
while(true){
try{
taskExecutor.execute(new MailSender(tUserIssueInfo));
break;
}catch(TaskRejectedException e){
try{
Thread.sleep(1000);
}catch(Exception e2){}
}
}
之後我又發現ThreadPoolTaskExecutor有一個成員方法可以設定RejectedExecption的丟擲規則 ,方法名是setRejectedExecutionHandler()。
什麼是RejectedExecutionHandler?
RejectedExecutionHandler handler: 用來拒絕一個任務的執行,有兩種情況會發生這種情況。
一是在execute方法中若addIfUnderMaximumPoolSize(command)為false,即執行緒池已經飽和;
二是在execute方法中, 發現runState!=RUNNING || poolSize == 0,即已經shutdown,就呼叫ensureQueuedTaskHandled(Runnable command),在該方法中有可能呼叫reject。
ThredPoolTaskExcutor的處理流程?
當池子大小小於corePoolSize,就新建執行緒,並處理請求
當池子大小等於corePoolSize,把請求放入workQueue中,池子裡的空閒執行緒就去workQueue中取任務並處理
當workQueue放不下任務時,就新建執行緒入池,並處理請求,如果池子大小撐到了maximumPoolSize,就用RejectedExecutionHandler來做拒絕處理
當池子的執行緒數大於corePoolSize時,多餘的執行緒會等待keepAliveTime長時間,如果無請求可處理就自行銷燬
其會優先建立 CorePoolSiz 執行緒, 當繼續增加執行緒時,先放入Queue中,當 CorePoolSiz 和 Queue 都滿的時候,就增加建立新執行緒,當執行緒達到MaxPoolSize的時候,就會丟擲錯 誤 org.springframework.core.task.TaskRejectedException
另外MaxPoolSize的設定如果比系統支援的執行緒數還要大時,會丟擲java.lang.OutOfMemoryError: unable to create new native thread 異常。
Reject策略預定義有四種:
(1)ThreadPoolExecutor.AbortPolicy策略,是預設的策略,處理程式遭到拒絕將丟擲執行時 RejectedExecutionException。
(2)ThreadPoolExecutor.CallerRunsPolicy策略 ,呼叫者的執行緒會執行該任務,如果執行器已關閉,則丟棄.
(3)ThreadPoolExecutor.DiscardPolicy策略,不能執行的任務將被丟棄.
(4)ThreadPoolExecutor.DiscardOldestPolicy策略,如果執行程式尚未關閉,則位於工作佇列頭部的任務將被刪除,然後重試執行程式(如果再次失敗,則重複此過程).