1. 程式人生 > >有界、無界佇列對ThreadPoolExcutor執行的影響

有界、無界佇列對ThreadPoolExcutor執行的影響

Java提供了4種執行緒池:
newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor、newScheduledThreadPool,你可以通過Executors來例項化這四種執行緒池。這四種執行緒池都直接或者間接獲取的ThreadPoolExecutor例項 ,只是例項化時傳遞的引數不一樣。所以如果java提供的四種執行緒池滿足不了我們的需求,我們可以建立自定義執行緒池。
ThreadPoolExecutor的構造方法如下:

這裡寫圖片描述

其中:
corePoolSize: 核心池的大小。 當有任務來之後,就會建立一個執行緒去執行任務,當執行緒池中的執行緒數目達到corePoolSize後,就會把到達的任務放到快取隊列當中;
maximumPoolSize: 執行緒池最大執行緒數,它表示線上程池中最多能建立多少個執行緒;
keepAliveTime: 表示執行緒沒有任務執行時最多保持多久時間會終止;
unit: 引數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:

TimeUnit.DAYS;               //天  
TimeUnit.HOURS;             //小時  
TimeUnit.MINUTES;           //分鐘  
TimeUnit.SECONDS;           //秒  
TimeUnit.MILLISECONDS;      //毫秒  
TimeUnit.MICROSECONDS;      //微妙  
TimeUnit.NANOSECONDS;       //納秒  

workQueue: 一個阻塞佇列,用來儲存等待執行的任務。 一般來說,這裡的阻塞佇列有以下幾種選擇:

ArrayBlockingQueue;    
LinkedBlockingQueue;    
SynchronousQueue  

threadFactory: 執行緒工廠,主要用來建立執行緒;
handler: 表示當拒絕處理任務時的策略,有以下四種取值:
ThreadPoolExecutor.AbortPolicy:丟棄任務並丟擲RejectedExecutionException異常。
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不丟擲異常。
ThreadPoolExecutor.DiscardOldestPolicy:丟棄佇列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:只要執行緒池不關閉,該策略直接在呼叫者執行緒中,運行當前被丟棄的任務

這裡寫圖片描述

以下主要講解儲存等待執行的任務的佇列對執行緒池執行的影響。

1.有界佇列

1.初始的poolSize < corePoolSize,提交的runnable任務,會直接做為new一個Thread的引數,立馬執行。
2.當提交的任務數超過了corePoolSize,會將當前的runable提交到一個block queue中。
3.有界佇列滿了之後,如果poolSize < maximumPoolsize時,會嘗試new 一個Thread的進行救急處理,立馬執行對應的runnable任務。
4.如果3中也無法處理了,就會走到第四步執行reject操作。

public class ThreadPoolExcutorTest implements Runnable {  


    public String name;  

    public ThreadPoolExcutorTest(String name) {  
        this.name = name;  
    }  

    @Override  
    public void run() {  
        System.out.println(name);  
        try {  
            Thread.sleep(1000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  

    }  

    public static void main(String[] args) {  
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(3);  
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(  
                            1, //corePoolSize  
                            2,  //maximumPoolSize  
                            1L,  
                            TimeUnit.SECONDS,  
                            workQueue  
                            );  
        threadPool.execute(new ThreadPoolExcutorTest("任務1"));  
        threadPool.execute(new ThreadPoolExcutorTest("任務2"));  
        threadPool.execute(new ThreadPoolExcutorTest("任務3"));  
        threadPool.execute(new ThreadPoolExcutorTest("任務4"));  
        threadPool.execute(new ThreadPoolExcutorTest("任務5"));  
        threadPool.execute(new ThreadPoolExcutorTest("任務6"));  
        threadPool.shutdown();  

    }  
}  

分析:執行緒池的corePoolSize為1,任務1提交後,執行緒開始執行,corePoolSize 數量用完,接著任務2、3、4提交,放到了有界佇列中,此時有界佇列也滿了。繼續提交任務5,由於當前執行的執行緒數poolSize < maximumPoolsize,執行緒池嘗試new一個新的執行緒來執行任務5,所以任務5會接著執行。當繼續提交任務6,時,poolSize達到了maximumPoolSize,有界佇列也滿了,所以執行緒池執行了拒絕操作。

2.無界佇列

與有界佇列相比,除非系統資源耗盡,否則無界的任務佇列不存在任務入隊失敗的情況。當有新的任務到來,系統的執行緒數小於corePoolSize時,則新建執行緒執行任務。當達到corePoolSize後,就不會繼續增加,若後續仍有新的任務加入,而沒有空閒的執行緒資源,則任務直接進入佇列等待。若任務建立和處理的速度差異很大,無界佇列會保持快速增長,直到耗盡系統記憶體。

public class ThreadPoolExcutorTest2 implements Runnable {  

    public Integer count;  

    public ThreadPoolExcutorTest2(Integer count) {  
        this.count = count;  
    }  

    @Override  
    public void run() {  
        System.out.println("任務" + count);  
        try {  
            Thread.sleep(2000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
    }  

    public static void main(String[] args) throws InterruptedException {  
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();  

        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 1L, TimeUnit.SECONDS, workQueue);  
        for (int i = 1; i <= 20; i++) {  
            pool.execute(new ThreadPoolExcutorTest2(i));  
        }  
        Thread.sleep(1000);  
        System.out.println("執行緒池中佇列中的執行緒數量:" + workQueue.size());  


        pool.shutdown();  
    }  
}  

如果修改了執行緒池的maximumPoolSize引數(大於corePoolSize的大小),程式執行結果不受影響。所以對於無界佇列,maximumPoolSize的設定設定的再大對於執行緒的執行是沒有影響的
Ps:這裡說LinkedBlockingQueue是無界佇列是不恰當的,只不過如果用無參建構函式初始化,預設的容量是Integer.MAX_VALUE

總結

當執行緒池的任務快取佇列已滿並且執行緒池中的執行緒數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略。