Java自帶的線程池ThreadPoolExecutor詳細介紹說明和實例應用

原文:http://my.oschina.net/sunjun/blog/9017

引用

【轟隆隆】 的 Java自帶的線程池ThreadPoolExecutor詳細介紹說明和實例應用

來源:YidingHe's Blog

從 Java 5 開始,Java 提供了自己的線程池。線程池就是一個線程的容器,每次只執行額定數量的線程。 java.util.concurrent.ThreadPoolExecutor 就是這樣的線程池。它很靈活,但使用起來也比較複雜,本文就對其做一個介紹。


首先是構造函數。以最簡單的構造函數爲例:


public ThreadPoolExecutor(   
            int corePoolSize,   
            int maximumPoolSize,   
            long keepAliveTime,   
            TimeUnit unit,   
            BlockingQueue workQueue)  
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)

看起來挺複雜的。這裏介紹一下。


corePoolSize 指的是保留的線程池大小。 
maximumPoolSize 指的是線程池的最大大小。 
keepAliveTime 指的是空閒線程結束的超時時間。 
unit 是一個枚舉,表示 keepAliveTime 的單位。 
workQueue 表示存放任務的隊列。

我們可以從線程池的工作過程中瞭解這些參數的意義。線程池的工作過程如下:


1、線程池剛創建時,裏面沒有一個線程。任務隊列是作爲參數傳進來的。不過,就算隊列裏面有任務,線程池也不會馬上執行它們。

2、當調用 execute() 方法添加一個任務時,線程池會做如下判斷:

    a. 如果正在運行的線程數量小於 corePoolSize,那麼馬上創建線程運行這個任務;

    b. 如果正在運行的線程數量大於或等於 corePoolSize,那麼將這個任務放入隊列。

    c. 如果這時候隊列滿了,而且正在運行的線程數量小於 maximumPoolSize,那麼還是要創建線程運行這個任務;

    d. 如果隊列滿了,而且正在運行的線程數量大於或等於 maximumPoolSize,那麼線程池會拋出異常,告訴調用者“我不能再接受任務了”。

3、當一個線程完成任務時,它會從隊列中取下一個任務來執行。

4、當一個線程無事可做,超過一定的時間(keepAliveTime)時,線程池會判斷,如果當前運行的線程數大於 corePoolSize,那麼這個線程就被停掉。所以線程池的所有任務完成後,它最終會收縮到 corePoolSize 的大小。

這樣的過程說明,並不是先加入任務就一定會先執行。假設隊列大小爲 10,corePoolSize 爲 3,maximumPoolSize 爲 6,那麼當加入 20 個任務時,執行的順序就是這樣的:首先執行任務 1、2、3,然後任務 4~13 被放入隊列。這時候隊列滿了,任務 14、15、16 會被馬上執行,而任務 17~20 則會拋出異常。最終順序是:1、2、3、14、15、16、4、5、6、7、8、9、10、11、12、13。下面是一個線程池使用的例子:


public static void main(String[] args) {   
    BlockingQueue queue = new LinkedBlockingQueue();   
    ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);   
    
    for (int i = 0; i < 20; i++) {   
        executor.execute(new Runnable() {   
    
            public void run() {   
                try {   
                    Thread.sleep(1000);   
                } catch (InterruptedException e) {   
                    e.printStackTrace();   
                }   
                System.out.println(String.format("thread %d finished", this.hashCode()));   
            }   
        });   
    }   
    executor.shutdown();   
}  

對這個例子的說明如下:

1、BlockingQueue 只是一個接口,常用的實現類有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好處在於沒有大小限制。這樣的話,因爲隊列不會滿,所以 execute() 不會拋出異常,而線程池中運行的線程數也永遠不會超過 corePoolSize 個,keepAliveTime 參數也就沒有意義了。

2、shutdown() 方法不會阻塞。調用 shutdown() 方法之後,主線程就馬上結束了,而線程池會繼續運行直到所有任務執行完纔會停止。如果不調用 shutdown() 方法,那麼線程池會一直保持下去,以便隨時添加新的任務。

到這裏對於這個線程池還只是介紹了一小部分。ThreadPoolExecutor 具有很強的可擴展性,不過擴展它的前提是要熟悉它的工作方式。

 

java.util.concurrent.ThreadPoolExecutor 類提供了豐富的可擴展性。你可以通過創建它的子類來自定義它的行爲。例如,我希望當每個任務結束之後打印一條消息,但我又無法修改任務對象,那麼我可以這樣寫:

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        System.out.println("Task finished.");
    }
};

 

除了 afterExecute 方法之外,ThreadPoolExecutor 類還有 beforeExecute() 和 terminated() 方法可以重寫,分別是在任務執行之前和整個線程池停止之後執行。



除了可以添加任務執行前後的動作之外, ThreadPoolExecutor 還允許你自定義當添加任務失敗後的執行策略。你可以調用線程池的 setRejectedExecutionHandler() 方法,用自定義的 RejectedExecutionHandler 對象替換現有的策略。 ThreadPoolExecutor 提供 4 個現有的策略,分別是:


ThreadPoolExecutor.AbortPolicy:表示拒絕任務並拋出異常 
ThreadPoolExecutor.DiscardPolicy:表示拒絕任務但不做任何動作 
ThreadPoolExecutor.CallerRunsPolicy:表示拒絕任務,並在調用者的線程中直接執行該任務 
ThreadPoolExecutor.DiscardOldestPolicy:表示先丟棄任務隊列中的第一個任務,然後把這個任務加進隊列。 

這裏是一個例子:

 

ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue);

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());

 


除此之外,你也可以通過實現 RejectedExecutionHandler 接口來編寫自己的策略。下面是一個例子:

ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.SECONDS, queue,
        new RejectedExecutionHandler() {
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println(String.format("Task %d rejected.", r.hashCode()));
            }
        }

);


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章