转: java多线程-ThreadPoolExecutor的拒绝策略RejectedExecutionHandler
轉自:? https://blog.csdn.net/qq_25806863/article/details/71172823
?
概述
原文地址 http://blog.csdn.net/qq_25806863/article/details/71172823
在分析ThreadPoolExecutor的構造參數時,有一個RejectedExecutionHandler參數。
RejectedExecutionHandler是一個接口:
public interface RejectedExecutionHandler {
??? void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
??? 1
??? 2
??? 3
里面只有一個方法。當要創建的線程數量大于線程池的最大線程數的時候,新的任務就會被拒絕,就會調用這個接口里的這個方法。
可以自己實現這個接口,實現對這些超出數量的任務的處理。
ThreadPoolExecutor自己已經提供了四個拒絕策略,分別是CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy
這四個拒絕策略其實一看實現方法就知道很簡單。
AbortPolicy
ThreadPoolExecutor中默認的拒絕策略就是AbortPolicy。直接拋出異常。
private static final RejectedExecutionHandler defaultHandler =
??? new AbortPolicy();
??? 1
??? 2
下面是他的實現:
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());
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
很簡單粗暴,直接拋出個RejectedExecutionException異常,也不執行這個任務了。
測試
先自定義一個Runnable,給每個線程起個名字,下面都用這個Runnable
static class MyThread implements Runnable {
??????? String name;
??????? public MyThread(String name) {
??????????? this.name = name;
??????? }
??????? @Override
??????? public void run() {
??????????? try {
??????????????? Thread.sleep(2000);
??????????? } catch (InterruptedException e) {
??????????????? e.printStackTrace();
??????????? }
??????????? System.out.println("線程:"+Thread.currentThread().getName() +" 執行:"+name +"? run");
??????? }
??? }
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
??? 9
??? 10
??? 11
??? 12
??? 13
??? 14
??? 15
然后構造一個核心線程是1,最大線程數是2的線程池。拒絕策略是AbortPolicy
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 0,
??????? TimeUnit.MICROSECONDS,
??????? new LinkedBlockingDeque<Runnable>(2),
??????? new ThreadPoolExecutor.AbortPolicy());
??? 1
??? 2
??? 3
??? 4
for (int i = 0; i < 6; i++) {
??? System.out.println("添加第"+i+"個任務");
??? executor.execute(new MyThread("線程"+i));
??? Iterator iterator = executor.getQueue().iterator();
??? while (iterator.hasNext()){
??????? MyThread thread = (MyThread) iterator.next();
??????? System.out.println("列表:"+thread.name);
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
??? 9
輸出是:
這里寫圖片描述
分析一下過程。
??? 添加第一個任務時,直接執行,任務列表為空。
??? 添加第二個任務時,因為采用的LinkedBlockingDeque,,并且核心線程正在執行任務,所以會將第二個任務放在隊列中,隊列中有 線程2.
??? 添加第三個任務時,也一樣會放在隊列中,隊列中有 線程2,線程3.
??? 添加第四個任務時,因為核心任務還在運行,而且任務隊列已經滿了,所以胡直接創建新線程執行第四個任務,。這時線程池中一共就有兩個線程在運行了,達到了最大線程數。任務隊列中還是有線程2, 線程3.
??? 添加第五個任務時,再也沒有地方能存放和執行這個任務了,就會被線程池拒絕添加,執行拒絕策略的rejectedExecution方法,這里就是執行AbortPolicy的rejectedExecution方法直接拋出異常。
??? 最終,只有四個線程能完成運行。后面的都被拒絕了。
CallerRunsPolicy
CallerRunsPolicy在任務被拒絕添加后,會調用當前線程池的所在的線程去執行被拒絕的任務。
下面說他的實現:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
??? public CallerRunsPolicy() { }
??? public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
??????? if (!e.isShutdown()) {
??????????? r.run();
??????? }
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
也很簡單,直接run。
測試
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
??????? TimeUnit.SECONDS,
??????? new LinkedBlockingDeque<Runnable>(2),
??????? new ThreadPoolExecutor.AbortPolicy());
??? 1
??? 2
??? 3
??? 4
按上面的運行,輸出
這里寫圖片描述
注意在添加第五個任務,任務5 的時候,同樣被線程池拒絕了,因此執行了CallerRunsPolicy的rejectedExecution方法,這個方法直接執行任務的run方法。因此可以看到任務5是在main線程中執行的。
從中也可以看出,因為第五個任務在主線程中運行,所以主線程就被阻塞了,以至于當第五個任務執行完,添加第六個任務時,前面兩個任務已經執行完了,有了空閑線程,因此線程6又可以添加到線程池中執行了。
這個策略的缺點就是可能會阻塞主線程。
DiscardPolicy
這個策略的處理就更簡單了,看一下實現就明白了:
public static class DiscardPolicy implements RejectedExecutionHandler {
??? public DiscardPolicy() { }
??? public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
這個東西什么都沒干。
因此采用這個拒絕策略,會讓被線程池拒絕的任務直接拋棄,不會拋異常也不會執行。
測試
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
??????? TimeUnit.SECONDS,
??????? new LinkedBlockingDeque<Runnable>(2),
??????? new ThreadPoolExecutor.DiscardPolicy());
??? 1
??? 2
??? 3
??? 4
輸出:
這里寫圖片描述
可以看到 后面添加的任務5和6根本不會執行,什么反應都沒有,直接丟棄。
DiscardOldestPolicy
DiscardOldestPolicy策略的作用是,當任務唄拒絕添加時,會拋棄任務隊列中最舊的任務也就是最先加入隊列的,再把這個新任務添加進去。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
??? public DiscardOldestPolicy() { }
??? public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
??????? if (!e.isShutdown()) {
??????????? e.getQueue().poll();
??????????? e.execute(r);
??????? }
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
??? 7
??? 8
??? 9
在rejectedExecution先從任務隊列總彈出最先加入的任務,空出一個位置,然后再次執行execute方法把任務加入隊列。
測試
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
??????? TimeUnit.SECONDS,
??????? new LinkedBlockingDeque<Runnable>(2),
??????? new ThreadPoolExecutor.DiscardOldestPolicy());
??? 1
??? 2
??? 3
??? 4
輸出是:
這里寫圖片描述
可以看到,
??? 在添加第五個任務時,會被線程池拒絕。這時任務隊列中有 任務2,任務3
??? 這時,拒絕策略會讓任務隊列中最先加入的任務彈出,也就是任務2.
??? 然后把被拒絕的任務5添加人任務隊列,這時任務隊列中就成了 任務3,任務5.
??? 添加第六個任務時會因為同樣的過程,將隊列中的任務3拋棄,把任務6加進去,任務隊列中就成了 任務5,任務6
??? 因此,最終能被執行的任務只有1,4,5,6. 任務2和任務3倍拋棄了,不會執行。
自定義拒絕策略
通過看前面的系統提供的四種拒絕策略可以看出,拒絕策略的實現都非常簡單。自己寫亦一樣
比如現在想讓被拒絕的任務在一個新的線程中執行,可以這樣寫:
static class MyRejectedExecutionHandler implements RejectedExecutionHandler {
??? @Override
??? public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
??????? new Thread(r,"新線程"+new Random().nextInt(10)).start();
??? }
}
??? 1
??? 2
??? 3
??? 4
??? 5
??? 6
然后正常使用:
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 2, 30,
??????? TimeUnit.SECONDS,
??????? new LinkedBlockingDeque<Runnable>(2),
??????? new MyRejectedExecutionHandler());
??? 1
??? 2
??? 3
??? 4
輸出:
這里寫圖片描述
發現被拒絕的任務5和任務6都在新線程中執行了。
?
總結
以上是生活随笔為你收集整理的转: java多线程-ThreadPoolExecutor的拒绝策略RejectedExecutionHandler的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux架设服务器相关书籍(linux
- 下一篇: (思科anti ddos)