最近工作種常用到ThreadPoolExecutor這個對象, 這是一個並發編程中非常常用的對象。因為和並發編程相關所以它存在於java.util.concurrent這包中。
創建這個對象的基本方法如下:
今天主要想研究一下最后一個參數RejectedExecutionHandler對整個線程池的影響。首先寫出需要用到測試代碼如下:
import java.io.Serializable; import java.util.concurrent.*; public class ThreadPoolExecutorTest { private static int produceTaskSleepTime = 1000; // 一秒add一個任務 private static int consumeTaskSleepTime = 60000; //每個任務停留10秒這樣結果更明顯清楚 private static BlockingQueue<Runnable> queue = new ArrayBlockingQueue<Runnable>(2); //緩存隊列數 private static int produceTaskMaxNumber = 51; //總線程數 private static int corePoolSize = 2; // private static int maximumPoolSize = 4; private static int keepAliveTime = 5; public static void main(String[] args) { ThreadPoolExecutor threadPoolExcutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.DiscardPolicy()); //調整策略 for(int i=1; i< produceTaskMaxNumber;i++){ try{ String work = "Task@ " + i; System.out.println("put : " + work); threadPoolExcutor.execute(new ThreadPoolTask(work)); System.out.println("BlockQueue Size is " + queue.size()); //打印出緩存隊列中線程數 //等待一段時間方便看清楚線程處理順序 Thread.sleep(produceTaskSleepTime); }catch (Exception e){ e.printStackTrace(); } } } public static class ThreadPoolTask implements Runnable, Serializable{ private static final long serialVersionUID = 0; private Object threadPoolTaskData; ThreadPoolTask(Object work){ this.threadPoolTaskData = work; } @Override public void run() { System.out.println("start............" + threadPoolTaskData); //標記任務開始 try { Thread.sleep(consumeTaskSleepTime); }catch (Exception e){ e.printStackTrace(); } System.out.println("end.............." + threadPoolTaskData); //標記任務結束 threadPoolTaskData = null; } public Object getTask(){ return this.threadPoolTaskData; } } }
我的想法是模擬50個任務,每個任務執行60s,這樣就可以不斷有任務阻塞到緩存隊列,並在一定時間內達到線程池最大線程數,進而發現不同拒絕策略是在線程數超出線程池最大允許數量后是如何處理。
1.DiscardPolicy
put : Task@ 1 BlockQueue Size is 0 start............Task@ 1 put : Task@ 2 BlockQueue Size is 0 start............Task@ 2 put : Task@ 3 BlockQueue Size is 1 put : Task@ 4 BlockQueue Size is 2 put : Task@ 5 BlockQueue Size is 2 start............Task@ 5 put : Task@ 6 BlockQueue Size is 2 start............Task@ 6 put : Task@ 7 BlockQueue Size is 2
..........
.......... put : Task@ 50 BlockQueue Size is 2 end..............Task@ 1 start............Task@ 3 end..............Task@ 2 start............Task@ 4 end..............Task@ 5 end..............Task@ 6 end..............Task@ 3 end..............Task@ 4
從結果可以看出,線程裝載順序是
核心線程數(1,2滿)->緩存隊列數(3,4 滿)->最大線程數(5,6滿)->不執行。
結論:大於最大線程數以后的任務完全不執行。
2.AbortPolicy
put : Task@ 7 java.util.concurrent.RejectedExecutionException: Task com.dangkei.ThreadPoolExecutorTest$ThreadPoolTask@7cf10a6f rejected from java.util.concurrent.ThreadPoolExecutor@2ff4f00f[Running, pool size = 4, active threads = 4, queued tasks = 2, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source) at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source) at com.dangkei.ThreadPoolExecutorTest.main(ThreadPoolExecutorTest.java:28)
結論:運行結果前后相同但是 在put Task 7時,也就是超過最大線程數后每個線程都拋出RejectedExecutionException
3.DiscardOldestPolicy
put : Task@ 1 BlockQueue Size is 0 start............Task@ 1 put : Task@ 2 BlockQueue Size is 0 start............Task@ 2 put : Task@ 3 BlockQueue Size is 1 put : Task@ 4 BlockQueue Size is 2 put : Task@ 5 BlockQueue Size is 2 start............Task@ 5 put : Task@ 6 BlockQueue Size is 2 start............Task@ 6 put : Task@ 7 BlockQueue Size is 2 put : Task@ 8 BlockQueue Size is 2 ...... ...... put : Task@ 49 BlockQueue Size is 2 put : Task@ 50 BlockQueue Size is 2 end..............Task@ 1 start............Task@ 49 end..............Task@ 2 start............Task@ 50 end..............Task@ 5 end..............Task@ 6 end..............Task@ 49 end..............Task@ 50
開始覺得這個結果比較奇怪,后來反應過來。 所謂1,2 ,5,6最終都執行結束了但是3,4線程沒有開始。最后還有49,50也都結束.
結論,廢棄最舊的線程是廢棄掉緩存隊列里最舊的線程對 核心隊列(corePoolSize),和 (核心隊列+緩存隊列)< 線程隊列 < 最大線程數 中間這部分線程(5,6 )也沒影響。
由於新的線程任務不斷替換掉 緩存隊列里的任務, 所以 位於 blockingqueue里的任務始終無法執行。 只有最后兩個線程(499,50) 沒有被新的任務替換正常執行了。
4.CallerRunsPolicy
put : Task@ 1 BlockQueue Size is 0 start............Task@ 1 put : Task@ 2 BlockQueue Size is 0 start............Task@ 2 put : Task@ 3 BlockQueue Size is 1 put : Task@ 4 BlockQueue Size is 2 put : Task@ 5 BlockQueue Size is 2 start............Task@ 5 put : Task@ 6 BlockQueue Size is 2 start............Task@ 6 put : Task@ 7 start............Task@ 7 end..............Task@ 1 start............Task@ 3 end..............Task@ 2 start............Task@ 4 end..............Task@ 5 end..............Task@ 6 end..............Task@ 7 BlockQueue Size is 0 put : Task@ 8 BlockQueue Size is 1 start............Task@ 8 put : Task@ 9 start............Task@ 9 BlockQueue Size is 0 put : Task@ 10 BlockQueue Size is 1 put : Task@ 11 BlockQueue Size is 2 put : Task@ 12 start............Task@ 12 end..............Task@ 3 start............Task@ 10 end..............Task@ 4 start............Task@ 11 end..............Task@ 8 end..............Task@ 9 end..............Task@ 12 BlockQueue Size is 0 put : Task@ 13 BlockQueue Size is 1 start............Task@ 13 put : Task@ 14 BlockQueue Size is 1 put : Task@ 15 BlockQueue Size is 2 put : Task@ 16 BlockQueue Size is 2 start............Task@ 16 put : Task@ 17 start............Task@ 17 end..............Task@ 10 start............Task@ 14 end..............Task@ 11 start............Task@ 15 end..............Task@ 13 end..............Task@ 16 end..............Task@ 17 BlockQueue Size is 0 put : Task@ 18 BlockQueue Size is 0 start............Task@ 18 put : Task@ 19 BlockQueue Size is 1 put : Task@ 20 BlockQueue Size is 2 put : Task@ 21 BlockQueue Size is 2 start............Task@ 21 put : Task@ 22 start............Task@ 22 end..............Task@ 14 start............Task@ 19 end..............Task@ 15 start............Task@ 20 end..............Task@ 18 end..............Task@ 21 end..............Task@ 22 BlockQueue Size is 0 put : Task@ 23 BlockQueue Size is 1 start............Task@ 23 put : Task@ 24 BlockQueue Size is 1 put : Task@ 25 BlockQueue Size is 2 put : Task@ 26 BlockQueue Size is 2 start............Task@ 26 put : Task@ 27 start............Task@ 27 end..............Task@ 19 start............Task@ 24 end..............Task@ 20 start............Task@ 25 end..............Task@ 23 end..............Task@ 26 end..............Task@ 27 BlockQueue Size is 0 put : Task@ 28 start............Task@ 28 BlockQueue Size is 0 put : Task@ 29 BlockQueue Size is 1 start............Task@ 29 put : Task@ 30 BlockQueue Size is 1 put : Task@ 31 BlockQueue Size is 2 put : Task@ 32 start............Task@ 32 end..............Task@ 24 start............Task@ 30 end..............Task@ 25 start............Task@ 31 end..............Task@ 28 end..............Task@ 29 end..............Task@ 32 BlockQueue Size is 0 put : Task@ 33 BlockQueue Size is 1 start............Task@ 33 put : Task@ 34 BlockQueue Size is 1 put : Task@ 35 BlockQueue Size is 2 put : Task@ 36 BlockQueue Size is 2 start............Task@ 36 put : Task@ 37 start............Task@ 37 end..............Task@ 30 start............Task@ 34 end..............Task@ 31 start............Task@ 35 end..............Task@ 33 end..............Task@ 36 end..............Task@ 37 BlockQueue Size is 0 put : Task@ 38 start............Task@ 38 BlockQueue Size is 0 put : Task@ 39 BlockQueue Size is 1 put : Task@ 40 BlockQueue Size is 2 put : Task@ 41 BlockQueue Size is 2 start............Task@ 41 put : Task@ 42 start............Task@ 42 end..............Task@ 34 start............Task@ 39 end..............Task@ 35 start............Task@ 40 end..............Task@ 38 end..............Task@ 41 end..............Task@ 42 BlockQueue Size is 0 put : Task@ 43 start............Task@ 43 BlockQueue Size is 0 put : Task@ 44 BlockQueue Size is 1 put : Task@ 45 BlockQueue Size is 2 put : Task@ 46 BlockQueue Size is 2 start............Task@ 46 put : Task@ 47 start............Task@ 47 end..............Task@ 39 start............Task@ 44 end..............Task@ 40 start............Task@ 45 end..............Task@ 43 end..............Task@ 46 end..............Task@ 47 BlockQueue Size is 0 put : Task@ 48 BlockQueue Size is 1 start............Task@ 48 put : Task@ 49 BlockQueue Size is 1 put : Task@ 50 BlockQueue Size is 2 end..............Task@ 44 start............Task@ 49 end..............Task@ 45 start............Task@ 50 end..............Task@ 48
這次貼出的是比較完整的打印結果:
結論:可以看到使用這種策略是不會丟失任何任務或者拋出任何異常的。 適應於對數據要求比較嚴謹的任務。
從這段結果還有個有意思的發現 其實使用這種策略是可以同時執行最大線程數+1個線程。
通過這次實驗得到以下總結:
假設我們可以把線程池執行看成三個隊列, 核心執行隊列, 緩存隊列, 最大執行隊列。 緩存隊列不是執行隊列。
一. 它們的裝載順序時這樣的。
核心-緩存-最大。
二. 但是執行優先級這樣的
核心-最大(然后緩存在所有線程中的任務執行完后進行補充)
三. DiscardPolicy策略
超出最大線程數后的任務將都被廢棄不會加入到緩存隊列,不拋異常。
四. AbordPolicy策略
超出最大線程數后的任務將被廢棄,拋出異常。
五. DiscardOldestPolicy策略
超出最大線程數后的任務將頂替緩存隊列中最早的任務。不拋異常但是被頂替掉的任務將丟失不執行。
六. CallerRunsPolicy策略
超出最大線程數后的任務將進行緩存隊列等待,緩存隊列所有任務輸送給執行隊列完畢清空后,新任務進入緩存隊列。
以上屬於個人理解, 有不對的地方請指正。 希望對大家學習能有幫助。