java-自定義線程池
當我們使用 線程池的時候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其實我們深入到這些方法裡面,就可以看到它們的是實作方式是這樣的。
1 public static ExecutorService newCachedThreadPool() {
2 return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
3 60L, TimeUnit.SECONDS,
4 new SynchronousQueue<Runnable>());
5 }
1 public static ExecutorService newFixedThreadPool(int nThreads) {
2 return new ThreadPoolExecutor(nThreads, nThreads,
3 0L, TimeUnit.MILLISECONDS,
4 new LinkedBlockingQueue<Runnable>());
5 }
包括其他幾種不同類型的線程池,其實都是通過 ThreadPoolExecutor這個核心類來建立的,如果我們要自定義線程池,那麼也是通過這個類來實作的。

該類有四個構造方法,檢視源碼可以看到,頭三個構造方法,其實都是調用的第四個構造方法,是以我們就解釋一下第四個構造方法的參數含義。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心線程池的大小,線上程池被建立之後,其實裡面是沒有線程的。(當然,調用prestartAllCoreThreads()或者prestartCoreThread()方法會預建立線程,而不用等着任務的到來)。當有任務進來的時候,才會建立線程。當線程池中的線程數量達到corePoolSize之後,就把任務放到 緩存隊列當中。(就是 workQueue)。
maximumPoolSize:最大線程數量是多少。它标志着這個線程池的最大線程數量。如果沒有最大數量,當建立的線程數量達到了 某個極限值,到最後記憶體肯定就爆掉了。
keepAliveTime:當線程沒有任務時,最多保持的時間,超過這個時間就被終止了。預設情況下,隻有 線程池中線程數量 大于 corePoolSize時,keepAliveTime值才會起作用。也就說說,隻有線上程池線程數量超出corePoolSize了。我們才會把逾時的空閑線程給停止掉。否則就保持線程池中有 corePoolSize 個線程就可以了。
Unit:參數keepAliveTime的時間機關,就是 TimeUnit類當中的幾個屬性。
如下圖:
workQueue:用來存儲待執行任務的隊列,不同的線程池它的隊列實作方式不同(因為這關系到排隊政策的問題)比如有以下幾種
ArrayBlockingQueue:基于數組的隊列,建立時需要指定大小。
LinkedBlockingQueue:基于連結清單的隊列,如果沒有指定大小,則預設值是 Integer.MAX_VALUE。(newFixedThreadPool和newSingleThreadExecutor使用的就是這種隊列)。
SynchronousQueue:這種隊列比較特殊,因為不排隊就直接建立新線程把任務送出了。(newCachedThreadPool使用的就是這種隊列)。
threadFactory:線程工廠,用來建立線程。
Handler:拒絕執行任務時的政策,一般來講有以下四種政策,
(1) ThreadPoolExecutor.AbortPolicy 丢棄任務,并抛出 RejectedExecutionException 異常。
(2) ThreadPoolExecutor.CallerRunsPolicy:該任務被線程池拒絕,由調用 execute方法的線程執行該任務。
(3) ThreadPoolExecutor.DiscardOldestPolicy : 抛棄隊列最前面的任務,然後重新嘗試執行任務。
(4) ThreadPoolExecutor.DiscardPolicy,丢棄任務,不過也不抛出異常。
看一個demo ,示例代碼位址:src/thread_runnable/CustomThreadPool.java
1 class CustomTask implements Runnable{
2 private int id;
3 public CustomTask(int id) {
4 this.id = id;
5 }
6
7 @Override
8 public void run() {
9 // TODO Auto-generated method stub
10 System.out.println("#" + id + " threadId=" + Thread.currentThread().getName() );
11 try {
12 TimeUnit.MILLISECONDS.sleep(100);
13 }catch(InterruptedException e){
14 e.printStackTrace();
15 }
16 }
17
18 }
19
20 public class CustomThreadPool {
21 public static void main(String[] args) {
22 // TODO Auto-generated method stub
23 BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);
24 ThreadPoolExecutor pool = new ThreadPoolExecutor(3, 5, 60, TimeUnit.MICROSECONDS, queue);
25
26 for (int i=0; i<7; i++){
27 Runnable task = new CustomTask(i);
28 pool.execute(task);
29 }
30
31 pool.shutdown();
32 }
33
34 }
輸出結果:
從這個例子,可以看出,雖然我們有7個任務,但是實際上,隻有三個線程在運作。
那麼當我們送出任務給線程池之後,它的處理政策是什麼呢?
(1),如果目前線程池線程數目小于 corePoolSize(核心池還沒滿呢),那麼就建立一個新線程去處理任務。
(2),如果核心池已經滿了,來了一個新的任務後,會嘗試将其添加到任務隊列中,如果成功,則等待空閑線程将其從隊列中取出并且執行,如果隊列已經滿了,則繼續下一步。
(3),此時,如果線程池線程數量 小于 maximumPoolSize,則建立一個新線程執行任務,否則,那就說明線程池到了最大飽和能力了,沒辦法再處理了,此時就按照拒絕政策來處理。(就是構造函數當中的Handler對象)。
(4),如果線程池的線程數量大于corePoolSize,則當某個線程的空閑時間超過了keepAliveTime,那麼這個線程就要被銷毀了,直到線程池中線程數量不大于corePoolSize為止。
舉個通俗易懂的例子,公司要設立一個項目組來處理某些任務,hr部門給的人員編制是10個人(corePoolSize)。同時給他們專門設定了一間有15個座位(maximumPoolSize)的辦公室。最開始的時候來了一個任務,就招聘一個人。就這樣,一個一個的招聘,招滿了十個人,不斷有新的任務安排給這個項目組,每個人也在不停的接任務幹活。不過後來任務越來越多,十個人無法處理完了。其他的任務就隻能在走廊外面排隊了。後來任務越來越多,走廊的排隊隊伍也擠不下。然後隻好找找一些臨時工來幫助完成任務。因為辦公室隻有15個座位,是以它們最多也就隻能找5個臨時工。可是任務依舊越來越多,根本處理不完,那沒辦法,這個項目組隻好拒絕再接新任務。(拒絕的方式就是 Handler),最後任務漸漸的少了,大家都比較清閑了。是以就決定看大家表現,誰表現不好,誰就被清理出這個辦公室(空閑時間超過 keepAliveTime),直到 辦公室隻剩下10個人(corePoolSize),維持固定的人員編制為止。
關于線程池,ThreadPoolExecutor還提供了一些需要注意的方法:
(1) shutdown(),平滑的關閉線程池。(如果還有未執行完的任務,就等待它們執行完)。
(2) shutdownNow()。簡單粗暴的關閉線程池。(沒有執行完的任務也直接關閉)。
(3) setCorePoolSize()。設定/更改核心池的大小。
(4) setMaximumPoolSize(),設定/更改線程池中最大線程的數量限制。
這幾篇java多線程文章的demo代碼下載下傳位址
http://download.csdn.net/detail/yaowen369/9786452轉載作者:
www.yaoxiaowen.com