引用java
【轟隆隆】 的 Java自帶的線程池ThreadPoolExecutor詳細介紹說明和實例應用ThreadPoolExecutor executor = new ThreadPoolExecutor(size, maxSize, 1, TimeUnit.DAYS, queue) {
來源:YidingHe's Blogide
從 Java 5 開始,Java 提供了本身的線程池。線程池就是一個線程的容器,每次只執行額定數量的線程。 java.util.concurrent.ThreadPoolExecutor 就是這樣的線程池。它很靈活,但使用起來也比較複雜,本文就對其作一個介紹。函數
首先是構造函數。以最簡單的構造函數爲例:this
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue)
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) 線程看起來挺複雜的。這裏介紹一下。orm
corePoolSize 指的是保留的線程池大小。
maximumPoolSize 指的是線程池的最大大小。
keepAliveTime 指的是空閒線程結束的超時時間。
unit 是一個枚舉,表示 keepAliveTime 的單位。
workQueue 表示存聽任務的隊列。
對象咱們能夠從線程池的工做過程當中瞭解這些參數的意義。線程池的工做過程以下:blog
一、線程池剛建立時,裏面沒有一個線程。任務隊列是做爲參數傳進來的。不過,就算隊列裏面有任務,線程池也不會立刻執行它們。接口
二、當調用 execute() 方法添加一個任務時,線程池會作以下判斷:隊列
a. 若是正在運行的線程數量小於 corePoolSize,那麼立刻建立線程運行這個任務;
b. 若是正在運行的線程數量大於或等於 corePoolSize,那麼將這個任務放入隊列。
c. 若是這時候隊列滿了,並且正在運行的線程數量小於 maximumPoolSize,那麼仍是要建立線程運行這個任務;
d. 若是隊列滿了,並且正在運行的線程數量大於或等於 maximumPoolSize,那麼線程池會拋出異常,告訴調用者「我不能再接受任務了」。
三、當一個線程完成任務時,它會從隊列中取下一個任務來執行。
四、當一個線程無事可作,超過必定的時間(keepAliveTime)時,線程池會判斷,若是當前運行的線程數大於 corePoolSize,那麼這個線程就被停掉。因此線程池的全部任務完成後,它最終會收縮到 corePoolSize 的大小。
這樣的過程說明,並非先加入任務就必定會先執行。假設隊列大小爲 10,corePoolSize 爲 3,maximumPoolSize 爲 6,那麼當加入 20 個任務時,執行的順序就是這樣的:首先執行任務 一、二、3,而後任務 4~13 被放入隊列。這時候隊列滿了,任務 1四、1五、16 會被立刻執行,而任務 17~20 則會拋出異常。最終順序是:一、二、三、1四、1五、1六、四、五、六、七、八、九、十、十一、十二、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();
}對這個例子的說明以下:
一、BlockingQueue 只是一個接口,經常使用的實現類有 LinkedBlockingQueue 和 ArrayBlockingQueue。用 LinkedBlockingQueue 的好處在於沒有大小限制。這樣的話,由於隊列不會滿,因此 execute() 不會拋出異常,而線程池中運行的線程數也永遠不會超過 corePoolSize 個,keepAliveTime 參數也就沒有意義了。
二、shutdown() 方法不會阻塞。調用 shutdown() 方法以後,主線程就立刻結束了,而線程池會繼續運行直到全部任務執行完纔會中止。若是不調用 shutdown() 方法,那麼線程池會一直保持下去,以便隨時添加新的任務。
到這裏對於這個線程池還只是介紹了一小部分。ThreadPoolExecutor 具備很強的可擴展性,不過擴展它的前提是要熟悉它的工做方式。
java.util.concurrent.ThreadPoolExecutor 類提供了豐富的可擴展性。你能夠經過建立它的子類來自定義它的行爲。例如,我但願當每一個任務結束以後打印一條消息,但我又沒法修改任務對象,那麼我能夠這樣寫:
除了 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()));
}
}
);
【轟隆隆】推薦: