ThreadPoolExecutor之RejectedExecutionHandler

最近工做種經常使用到ThreadPoolExecutor這個對象, 這是一個併發編程中很是經常使用的對象。由於和併發編程相關因此它存在於java.util.concurrent這包中。java

建立這個對象的基本方法以下:編程

 

 今天主要想研究一下最後一個參數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.DiscardPolicyide

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滿)->不執行。測試

結論:大於最大線程數之後的任務徹底不執行。this

2.AbortPolicyspa

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策略

   超出最大線程數後的任務將進行緩存隊列等待,緩存隊列全部任務輸送給執行隊列完畢清空後,新任務進入緩存隊列。

 

 以上屬於我的理解, 有不對的地方請指正。 但願對你們學習能有幫助。

相關文章
相關標籤/搜索