Java 線程池 拒絕策略 RejectedExecutionHandler介紹

一、介紹

當Executor已經關閉(即執行了executorService.shutdown()方法後),並且Executor將有限邊界用於最大線程數量和工作隊列容量,且已經飽和時,在方法execute()中提交的新任務將被拒絕。

在以上述情況下,execute 方法將調用其 RejectedExecutionHandler 的 RejectedExecutionHandler.rejectedExecution(Java.lang.Runnable, java.util.concurrent.ThreadPoolExecutor) 方法。

首先我們來看下RejectedExecutionHandler 的定義:



下面提供了四種預定義的處理程序策略:

(1) 默認的ThreadPoolExecutor.AbortPolicy   處理程序遭到拒絕將拋出運行時RejectedExecutionException;
(2) ThreadPoolExecutor.CallerRunsPolicy 線程調用運行該任務的 execute 本身。此策略提供簡單的反饋控制機制,能夠減緩新任務的提交速度

(3) ThreadPoolExecutor.DiscardPolicy  不能執行的任務將被刪除;

(4) ThreadPoolExecutor.DiscardOldestPolicy  如果執行程序尚未關閉,則位於工作隊列頭部的任務將被刪除,然後重試執行程序(如果再次失敗,則重複此過程)。

線程池默認會採用的是defaultHandler策略。首先看defaultHandler的定義:



再看看具體的AbortPolicy:



看一下其他拒絕策略的具體實現:

CallerRunsPolicy實現類: 



DiscardPolicy實現類: 



DiscardOldestPolicy實現類: 



通過以上分析,我們都知道:這四個RejectedExecutionHandler的實現類都是ThreadPoolExecutor的靜態內部類。

二、測試案例

寫一個task:

  1. package com.npf.thread.test;  
  2.   
  3. public class Task implements Runnable {  
  4.   
  5.     protected String name;  
  6.       
  7.     public Task(String name) {  
  8.         super();  
  9.         this.name = name;  
  10.     }  
  11.   
  12.     @Override  
  13.     public void run() {  
  14.         try {  
  15.             System.out.println(this.name + " is running.");    
  16.             Thread.sleep(500);   
  17.         } catch (Exception e) {  
  18.               
  19.         }  
  20.     }  
  21.   
  22. }  

1. AbortPolicy 示例

  1. package com.npf.thread.test;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4. import java.util.concurrent.RejectedExecutionException;  
  5. import java.util.concurrent.ThreadPoolExecutor;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8. public class AbortPolicyDemo {  
  9.       
  10.     public static void main(String[] args) {  
  11.         // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1,"線程池"的阻塞隊列容量爲1。    
  12.         ThreadPoolExecutor pool = new ThreadPoolExecutor(110,  
  13.                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));  
  14.           
  15.         // 設置線程池的拒絕策略爲AbortPolicy    
  16.         pool.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());  
  17.           
  18.         try {  
  19.             // 新建10個任務,並將它們添加到線程池中    
  20.             for (int i = 0; i < 10; i++) {    
  21.                 Runnable myTask = new Task("task-"+i);    
  22.                 pool.execute(myTask);    
  23.             }    
  24.         } catch (RejectedExecutionException e) {  
  25.             e.printStackTrace();  
  26.              // 關閉線程池    
  27.             pool.shutdown();  
  28.         }  
  29.     }  
  30. }  
某一次運行結果,直接已異常的形式拋出:



2. CallerRunsPolicy 示例 

  1. package com.npf.thread.test;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4. import java.util.concurrent.ThreadPoolExecutor;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. public class CallerRunsPolicyDemo {  
  8.       
  9.     public static void main(String[] args) {  
  10.         // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1,"線程池"的阻塞隊列容量爲1。    
  11.         ThreadPoolExecutor pool = new ThreadPoolExecutor(110,  
  12.                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));  
  13.           
  14.         // 設置線程池的拒絕策略爲CallerRunsPolicy    
  15.         pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());  
  16.   
  17.         // 新建10個任務,並將它們添加到線程池中    
  18.        for (int i = 0; i < 10; i++) {    
  19.            Runnable myTask = new Task("task-"+i);    
  20.            pool.execute(myTask);    
  21.         }    
  22.   
  23.          // 關閉線程池    
  24.         pool.shutdown();  
  25.     }  
  26. }  

某一次運行結果,當有新任務添加到線程池被拒絕時,線程池會將被拒絕的任務添加到"線程池正在運行的線程"中去運行



3. DiscardPolicy 示例

  1. package com.npf.thread.test;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4. import java.util.concurrent.ThreadPoolExecutor;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. public class DiscardPolicyDemo {  
  8.       
  9.     public static void main(String[] args) {  
  10.         // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1,"線程池"的阻塞隊列容量爲1。    
  11.         ThreadPoolExecutor pool = new ThreadPoolExecutor(110,  
  12.                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));  
  13.           
  14.         // 設置線程池的拒絕策略爲DiscardPolicy    
  15.         pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());  
  16.   
  17.         // 新建10個任務,並將它們添加到線程池中    
  18.         for (int i = 0; i < 10; i++) {    
  19.             Runnable myTask = new Task("task-"+i);    
  20.            pool.execute(myTask);    
  21.         }  
  22.           
  23.         // 關閉線程池    
  24.         pool.shutdown();  
  25.     }  
  26. }  

某一次運行結果,線程池pool的"最大池大小"和"核心池大小"都爲1,這意味着"線程池能同時運行的任務數量最大隻能是1"。線程池pool的阻塞隊列是ArrayBlockingQueue,ArrayBlockingQueue是一個有界的阻塞隊列,ArrayBlockingQueue的容量爲1。這也意味着線程池的阻塞隊列只能有一個線程池阻塞等待。由此可知,線程池中共運行了2個任務。第1個任務直接放到Worker中,通過線程去執行;第2個任務放到阻塞隊列中等待。其他的任務都被丟棄了。



4. DiscardOldestPolicy 示例

  1. package com.npf.thread.test;  
  2.   
  3. import java.util.concurrent.ArrayBlockingQueue;  
  4. import java.util.concurrent.ThreadPoolExecutor;  
  5. import java.util.concurrent.TimeUnit;  
  6.   
  7. public class DiscardOldestPolicyDemo {  
  8.       
  9.     public static void main(String[] args) {  
  10.         // 創建線程池。線程池的"最大池大小"和"核心池大小"都爲1,"線程池"的阻塞隊列容量爲1。    
  11.         ThreadPoolExecutor pool = new ThreadPoolExecutor(110,  
  12.                 TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1));  
  13.           
  14.         // 設置線程池的拒絕策略爲DiscardOldestPolicy    
  15.         pool.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());  
  16.   
  17.         // 新建10個任務,並將它們添加到線程池中    
  18.         for (int i = 0; i < 10; i++) {    
  19.             Runnable myTask = new Task("task-"+i);    
  20.             pool.execute(myTask);    
  21.         }  
  22.           
  23.         // 關閉線程池    
  24.         pool.shutdown();  
  25.     }  
  26. }  

某一次運行結果,當有新任務添加到線程池被拒絕時,線程池會丟棄阻塞隊列中末尾的任務,然後將被拒絕的任務添加到末尾。

發佈了47 篇原創文章 · 獲贊 22 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章