在前面三篇文章中,我們已經對ThreadPoolExecutor的應用以及任務處理和生命週期相關的原始碼實現做了整理分析。這篇我們簡要整理下java.util.concurrent包中的RejectedExecutionHandler這個介面和對應的實現類。
0. RejectedExecutionHandler介面
當ThreadPoolExecutor執行任務的時候,如果執行緒池的執行緒已經飽和,並且任務佇列也已滿。那麼就會做丟棄處理,這也是execute()方法實現中的操作,原始碼如下:
1
2
|
else
if
(!addWorker(command, false )) reject(command); |
這個reject()方法很簡單,直接呼叫丟棄處理的handler方法的rejectedExecution()。
在java.util.concurrent中,專門為此定義了一個介面,是RejectedExecutionHandler:
1
2
3
|
public
interface
RejectedExecutionHandler { void
rejectedExecution(Runnable r, ThreadPoolExecutor executor); } |
其中只有rejectedExecution()一個方法。返回為void,而引數一個是具體的Runnable任務,另一個則是被提交任務的ThreadPoolExecutor。
凡是實現了這個方法的類都可以作為丟棄處理器在ThreadPoolExecutor物件構造的時候作為引數傳入,這個前面的文章已經提到過了。其中ThreadPoolExecutor給出了4種基本策略的實現。分別是:
- CallerRunsPolicy
- AbortPolicy
- DiscardPolicy
- DiscardOldestPolicy
下面分別詳細說明。
1. 直接丟棄
這個也是實現最簡單的類,其中的rejectedExecution()方法是空實現,即什麼也不做,那麼提交的任務將會被丟棄,而不做任何處理。
1
2
3
4
5
6
|
public
static
class
DiscardPolicy implements
RejectedExecutionHandler { public
DiscardPolicy() { } public
void
rejectedExecution(Runnable r, ThreadPoolExecutor e) { } } |
這個策略使用的時候要小心,要明確需求。不然不知不覺的任務就丟了。
2. 丟棄最老
和上面的有些類似,也是會丟棄掉一個任務,但是是佇列中最早的。
實現如下:
1
2
3
4
5
6
7
8
9
10
|
public
static
class
DiscardOldestPolicy implements
RejectedExecutionHandler { public
DiscardOldestPolicy() { } public
void
rejectedExecution(Runnable r, ThreadPoolExecutor e) { if
(!e.isShutdown()) { e.getQueue().poll(); e.execute(r); } } } |
注意,會先判斷ThreadPoolExecutor物件是否已經進入SHUTDOWN以後的狀態。之後取出佇列頭的任務並不做任何處理,即丟棄,再重新呼叫execute()方法提交新任務。
3. 廢棄終止
這個RejectedExecutionHandler類和直接丟棄不同的是,不是默默地處理,而是丟擲java.util.concurrent.RejectedExecutionException異常,這個異常是RuntimeException的子類。這個策略實現如下:
1
2
3
4
5
6
7
8
9
|
public
static
class
AbortPolicy implements
RejectedExecutionHandler { public
AbortPolicy() { } public
void
rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw
new
RejectedExecutionException( "Task
"
+ r.toString() + "
rejected from "
+ e.toString()); } } |
注意,處理這個異常的執行緒是執行execute()的呼叫者執行緒。
4. 呼叫者執行策略
在這個策略實現中,任務還是會被執行,但執行緒池中不會開闢新執行緒,而是提交任務的執行緒來負責維護任務。
1
2
3
4
5
|
public
void
rejectedExecution(Runnable r, ThreadPoolExecutor e) { if
(!e.isShutdown()) { r.run(); } } |
注意,和DiscardOldestPolicy同樣,也會先判斷ThreadPoolExecutor物件的狀態,之後執行任務。這樣處理的一個好處,是讓caller執行緒執行任務,以推遲該執行緒進一步提交新任務,有效的緩解了執行緒池物件飽和的情況。
上面只是SunJDK中提供的4種最基本策略,開發者可以根據具體需求定製。
此外,前文提到ThreadPoolExecutor也可以進行擴充套件。在java.util.concurrent包中有ScheduledThreadPoolExecutor這樣一個類,其擴充套件了ThreadPoolExecutor,實現了一些時間和任務排程相關的方法。這裡我就不具體整理了。