一. 線程池介紹
1.1 簡介
線程池是一種多線程處理形式,處理過程中將任務添加到隊列,然后在創建線程后自動啟動這些任務。線程池的基本思想還是一種對象池的思想,開辟一塊內存空間,里面存放了眾多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成后線程對象歸池,這樣可以避免反復創建線程對象所帶來的性能開銷,節省了系統的資源。
多線程技術主要解決處理器單元內多個線程執行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。
假設一個服務器完成一項任務所需時間為:T1 創建線程時間,T2 在線程中執行任務的時間,T3 銷毀線程時間。如果:T1 + T3 遠大於 T2,則可以采用線程池,以提高服務器性能。
線程池技術正是關注如何縮短或調整T1,T3時間的技術,從而提高服務器程序性能的。它把T1,T3分別安排在服務器程序的啟動和結束的時間段或者一些空閑的時間段,這樣在服務器程序處理客戶請求時,不會有T1,T3的開銷了。線程池不僅調整T1,T3產生的時間段,而且它還顯著減少了創建線程的數目,比如:
假設一個服務器一天要處理50000個請求,並且每個請求需要一個單獨的線程完成。在線程池中,線程數一般是固定的,所以產生線程總數不會超過線程池中線程的數目,而如果服務器不利用線程池來處理這些請求則線程總數為50000。一般線程池大小是遠小於50000。所以利用線程池的服務器程序不會為了創建50000而在處理請求時浪費時間,從而提高效率。
1.2 線程池優點
第一:降低資源消耗。通過重復利用已創建的線程降低線程創建和銷毀造成的消耗。
第二:提高響應速度。當任務到達時,任務可以不需要等到線程創建就能立即執行。
第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創建,不僅會消耗系統資源,還會降低系統的穩定性,使用線程池可以進行統一的分配,調優和監控。
二. 線程池的具體類
java中常用的線程池類主要有Executors類和ThreadPoolExecutor類。
2.1 Executors類
Executors類可以用於方便的創建線程池。它為Executor,ExecutorService,ScheduledExecutorService,ThreadFactory和Callable類提供了一些工具方法。Executors。在java doc中,並不提倡我們直接使用ThreadPoolExecutor,而是使用Executors類中提供的四個靜態方法來創建線程池:
2.1.1 newCachedThreadPool
創建一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則新建線程。
1 public static ExecutorService newCachedThreadPool() { 2 return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 3 60L, TimeUnit.SECONDS, 4 new SynchronousQueue<Runnable>()); 5 }
newCachedThreadPool將corePoolSize設置為0,將maximumPoolSize設置為Integer.MAX_VALUE,使用的SynchronousQueue,也就是說來了任務就創建線程運行,當線程空閑超過60秒,就銷毀線程。
2.1.2 newFixedThreadPool
創建一個定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
1 public static ExecutorService newFixedThreadPool(int nThreads) { 2 return new ThreadPoolExecutor(nThreads, nThreads, 3 0L, TimeUnit.MILLISECONDS, 4 new LinkedBlockingQueue<Runnable>()); 5 }
newFixedThreadPool創建的線程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue。
2.1.3 newScheduledThreadPool
創建一個定長線程池,支持定時及周期性任務執行。
1 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { 2 return new ScheduledThreadPoolExecutor(corePoolSize); 3 }
2.1.4 newSingleThreadExecutor
創建一個單線程化的線程池(容量為1的緩沖池),它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行。
1 public static ExecutorService newSingleThreadExecutor() { 2 return new FinalizableDelegatedExecutorService 3 (new ThreadPoolExecutor(1, 1, 4 0L, TimeUnit.MILLISECONDS, 5 new LinkedBlockingQueue<Runnable>())); 6 }
newSingleThreadExecutor將corePoolSize和maximumPoolSize都設置為1,也使用的LinkedBlockingQueue。
如果Executors提供的幾個靜態方法能滿足要求,就盡量使用它提供的三個方法,因為自己去手動配置ThreadPoolExecutor的參數有點麻煩,要根據實際任務的類型和數量來進行配置。另外,如果ThreadPoolExecutor達不到要求,可以自己繼承ThreadPoolExecutor類進行重寫。
2.2 ThreadPoolExecutor類
java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個類,因此如果要透徹地了解Java中的線程池,必須先了解這個類。
2.2.1 ThreadPoolExecutor類圖關系:
- Executor是一個頂層接口,在它里面只聲明了一個方法execute(Runnable),返回值為void,參數為Runnable類型,該方法用於接收執行用戶提交任務。
- ExecutorService 接口繼承了Executor接口,定義了線程池終止和創建及提交 futureTask 任務支持的方法。並聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等。
- AbstractExecutorService 是抽象類,它實現了ExecutorService接口及其中的的所有方法。主要實現了 ExecutorService 和 futureTask 相關的一些任務創建和提交的方法。
- ThreadPoolExecutor 繼承了類AbstractExecutorService,它是最核心的一個類,是線程池的內部實現。線程池的功能都在這里實現了,平時用的最多的基本就是這個。其源碼很精練,遠沒當時想象的多。
- ScheduledThreadPoolExecutor 在 ThreadPoolExecutor 的基礎上提供了支持定時調度的功能。線程任務可以在一定延時時間后才被觸發執行。
2.2.2 ThreadPoolExecutor的構造方法(4個)
1 public class ThreadPoolExecutor extends AbstractExecutorService { 2 ..... 3 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, 4 BlockingQueue<Runnable> workQueue); 5 6 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, 7 BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory); 8 9 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, 10 BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler); 11 12 public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, 13 BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler); 14 ... 15 }
ThreadPoolExecutor繼承了AbstractExecutorService類,並提供了四個構造器,事實上,通過觀察每個構造器的源碼具體實現,發現前面三個構造器都是調用的第四個構造器進行的初始化工作。
下面解釋下一下構造器中各個參數的含義:
- corePoolSize:核心池的大小,這個參數跟后面講述的線程池的實現原理有非常大的關系。在創建了線程池后,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池后,線程池中的線程數為0,當有任務來之后,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize后,就會把到達的任務放到緩存隊列當中。
- maximumPoolSize:線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程。
- keepAliveTime:表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime才會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閑的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數為0。
- unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:
1 TimeUnit.DAYS; //天 2 TimeUnit.HOURS; //小時 3 TimeUnit.MINUTES; //分鍾 4 TimeUnit.SECONDS; //秒 5 TimeUnit.MILLISECONDS; //毫秒 6 TimeUnit.MICROSECONDS; //微妙 7 TimeUnit.NANOSECONDS; //納秒
- workQueue:一個阻塞隊列,用來存儲等待執行的任務,這個參數的選擇也很重要,會對線程池的運行過程(線程池的排隊策略)產生重大影響,通常可以取下面三種類型ArrayBlockingQueue(基於數組的先進先出隊列,此隊列創建時必須指定大小)、LinkedBlockingQueue(基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認為Integer.MAX_VALUE)、SynchronousQueue(這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務)。一般使用LinkedBlockingQueue和Synchronous。
- threadFactory:線程工廠,主要用來創建線程;
- handler:表示當拒絕處理任務時的策略,有以下四種取值:
-
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程) ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
2.2.3 ThreadPoolExecutor類中重要的方法
execute() //execute()方法實際上是Executor中聲明的方法, //在ThreadPoolExecutor進行了具體的實現,這個方法是ThreadPoolExecutor的核心方法, //通過這個方法可以向線程池提交一個任務,交由線程池去執行。 submit() //submit()方法是在ExecutorService中聲明的方法, //在AbstractExecutorService就已經有了具體的實現,在ThreadPoolExecutor中並沒有對其進行重寫, //這個方法也是用來向線程池提交任務的,但是它和execute()方法不同,它能夠返回任務執行的結果, //去看submit()方法的實現,會發現它實際上還是調用的execute()方法,只不過它利用了Future來獲取任務執行結果。 shutdown() //用來關閉線程池 shutdownNow() //用來關閉線程池
除了這幾個比較重要的方法之外,ThreadPoolExecutor還有很多其他的方法,getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等。
三. 線程池實現原理(ThreadPoolExecutor)
3.1 ThreadPoolExecutor內部的幾個重要屬性
3.1.1 線程池狀態
在ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:
1 volatile int runState; 2 3 static final int RUNNING = 0; //當創建線程池后,初始時,線程池處於RUNNING狀態。 4 5 static final int SHUTDOWN = 1; //如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態, 6 //此時線程池不能夠接受新的任務,它會等待所有任務執行完畢。 7 static final int STOP = 2; //如果調用了shutdownNow()方法,則線程池處於STOP狀態, 8 //此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務。 9 static final int TERMINATED = 3; //當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷毀, 10 //任務緩存隊列已經清空或執行結束后,線程池被設置為TERMINATED狀態。
runState表示當前線程池的狀態,它是一個volatile變量用來保證線程之間的可見性。以上幾個static final變量表示runState可能的幾個取值。
3.1.2 等待任務隊列和工作集
1 private final BlockingQueue<Runnable> workQueue; //等待被執行的Runnable任務 2 private final HashSet<Worker> workers = new HashSet<Worker>(); //正在被執行的Worker任務集
3.1.3 線程池的主要狀態鎖
線程池內部的狀態變化 ( 比如線程池大小、runState等 ) 都需要基於此鎖。
private final ReentrantLock mainLock = new ReentrantLock();
3.1.4 線程的存活時間和大小
1 private volatile long keepAliveTime;// 線程存活時間 2 private volatile boolean allowCoreThreadTimeOut;// 是否允許核心線程存活 3 private volatile int corePoolSize;// 核心池大小 4 private volatile int maximumPoolSize; // 最大池大小 5 private volatile int poolSize; //當前池大小 6 private int largestPoolSize; //最大池大小,區別於maximumPoolSize,是用於記錄線程池曾經達到過的最大並發,理論上小於等於maximumPoolSize。
3.1.5 線程工廠和拒絕策略
1 private volatile RejectedExecutionHandler handler;// 拒絕策略,用於當線程池無法承載新線程是的處理策略。 2 private volatile ThreadFactory threadFactory;// 線程工廠,用於在線程池需要新創建線程的時候創建線程
3.1.6 線程池完成任務數和最大線程數
1 private long completedTaskCount;//線程池運行到當前完成的任務數總和 2 private int largestPoolSize; //用來記錄線程池中曾經出現過的最大線程數
對於corePoolSize、maximumPoolSize、largestPoolSize變量可借助與下面的例子幫助加深理解:
假如有一個工廠,工廠里面有10個工人,每個工人同時只能做一件任務。因此只要當10個工人中有工人是空閑的,來了任務就分配給空閑的工人做;當10個工人都有任務在做時,如果還來了任務,就把任務進行排隊等待;如果說新任務數目增長的速度遠遠大於工人做任務的速度,那么此時工廠主管可能會想補救措施,比如重新招4個臨時工人進來;然后就將任務也分配給這4個臨時工人做;如果說着14個工人做任務的速度還是不夠,此時工廠主管可能就要考慮不再接收新的任務或者拋棄前面的一些任務了。當這14個工人當中有人空閑時,而新任務增長的速度又比較緩慢,工廠主管可能就考慮辭掉4個臨時工了,只保持原來的10個工人,畢竟請額外的工人是要花錢的。
這個例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。也就是說corePoolSize就是線程池大小,maximumPoolSize在我看來是線程池的一種補救措施,即任務量突然過大時的一種補救措施。largestPoolSize只是一個用來起記錄作用的變量,用來記錄線程池中曾經有過的最大線程數目,跟線程池的容量沒有任何關系。
3.2 ThreadPoolExecutor的內部工作原理
ThreadPoolExecutor的內部工作原理總結起來就是 5 句話:
- 如果當前池大小 poolSize 小於 corePoolSize ,則創建新線程執行任務。
- 如果當前池大小 poolSize 大於 corePoolSize ,且等待隊列未滿,則進入等待隊列
- 如果當前池大小 poolSize 大於 corePoolSize 且小於 maximumPoolSize ,且等待隊列已滿,則創建新線程執行任務。
- 如果當前池大小 poolSize 大於 corePoolSize 且大於 maximumPoolSize ,且等待隊列已滿,則調用拒絕策略來處理該任務。
- 線程池里的每個線程執行完任務后不會立刻退出,而是會去檢查下等待隊列里是否還有線程任務需要執行,如果在 keepAliveTime 里等不到新的任務了,那么線程就會退出。
3.3 ThreadPoolExecutor源碼分析
在ThreadPoolExecutor類中,最核心的任務提交方法是execute()方法,雖然通過submit也可以提交任務,但是實際上submit方法里面最終調用的還是execute()方法:
1 public void execute(Runnable command) { 2 if (command == null) 3 throw new NullPointerException(); 4 if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { 5 if (runState == RUNNING && workQueue.offer(command)) { 6 if (runState != RUNNING || poolSize == 0) 7 ensureQueuedTaskHandled(command); 8 } 9 else if (!addIfUnderMaximumPoolSize(command)) 10 reject(command); // is shutdown or saturated 11 } 12 }
一個任務通過 execute(Runnable)方法被添加到線程池,任務就是一個Runnable類型的對象,任務的執行方法就是run()方法,如果傳入的為null,側拋出NullPointerException。
如果當前線程數小於corePoolSize,調用addIfUnderCorePoolSize方法,addIfUnderCorePoolSize方法首先調用mainLock加鎖,再次判斷當前線程數小於corePoolSize並且線程池處於RUNNING狀態,則調用addThread增加線程
addIfUnderCorePoolSize方法實現:
1 private boolean addIfUnderCorePoolSize(Runnable firstTask) { 2 Thread t = null; 3 final ReentrantLock mainLock = this.mainLock; 4 mainLock.lock(); 5 try { 6 if (poolSize < corePoolSize && runState == RUNNING) 7 t = addThread(firstTask); //創建線程去執行firstTask任務 8 } finally { 9 mainLock.unlock(); 10 } 11 if (t == null) 12 return false; 13 t.start(); 14 return true; 15 }
addThread方法首先創建Work對象,然后調用threadFactory創建新的線程,如果創建的線程不為null,將Work對象的thread屬性設置為此創建出來的線程,並將此Work對象放入workers中,然后在增加當前線程池的中線程數,增加后回到addIfUnderCorePoolSize方法 ,釋放mainLock,最后啟動這個新創建的線程來執行新傳入的任務。
addThread方法實現:
1 private Thread addThread(Runnable firstTask) { 2 Worker w = new Worker(firstTask); 3 Thread t = threadFactory.newThread(w); //創建一個線程,執行任務 4 if (t != null) { 5 w.thread = t; //將創建的線程的引用賦值為w的成員變量 6 workers.add(w); 7 int nt = ++poolSize; //當前線程數加1 8 if (nt > largestPoolSize) 9 largestPoolSize = nt; 10 } 11 return t; 12 }
從addThread方法看得出,Worker對象包裝了參數傳入的任務,threadFactory新創建的線程包裝了Worker對象,在執行新創建線程的run方法時,調用到了Worker對象的run方法。
Worker類最核心的run方法如下:
1 public void run() { 2 try { 3 Runnable task = firstTask; 4 firstTask = null; 5 while (task != null || (task = getTask()) != null) { 6 runTask(task); 7 task = null; 8 } 9 } finally { 10 workerDone(this); 11 } 12 }
從以上方法可以看出,Worker所在的線程啟動后,首先執行創建其時傳入的Runnable任務,執行完成后,循環調用getTask從任務緩存隊列里面去獲取新的任務,在沒有任務的情況下,退出此線程。
getTask方法的實現如下:
1 Runnable getTask() { 2 for (;;) { 3 try { 4 int state = runState; 5 if (state > SHUTDOWN) 6 return null; 7 Runnable r; 8 if (state == SHUTDOWN) // Help drain queue 9 r = workQueue.poll(); 10 else if (poolSize > corePoolSize || allowCoreThreadTimeOut) //如果線程數大於核心池大小或者允許為核心池線程設置空閑時間, 11 //則通過poll取任務,若等待一定的時間取不到任務,則返回null。 12 r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); 13 else 14 r = workQueue.take(); 15 if (r != null) 16 return r; 17 if (workerCanExit()) { //如果沒取到任務,即r為null,則判斷當前的worker是否可以退出 18 if (runState >= SHUTDOWN) // Wake up others 19 interruptIdleWorkers(); //中斷處於空閑狀態的worker 20 return null; 21 } 22 // Else retry 23 } catch (InterruptedException ie) { 24 // On interruption, re-check runState 25 } 26 } 27 }
getTask就是通過WorkQueue的poll或task方法來獲取下一個要執行的任務。它先判斷當前線程池狀態,如果runState大於SHUTDOWN(即為STOP或者TERMINATED),則直接返回null。如果runState為SHUTDOWN或者RUNNING,則從任務緩存隊列取任務。
回到execute方法代碼的5-10行。如果當前線程池數量大於corePoolSize或addIfUnderCorePoolSize方法執行失敗,則執行后續操作;如果線程池處於運行狀態並且workQueue中成功加入任務,再次判斷如果線程池的狀態不為運行狀態或當前線程池數為0,則調用ensureQueuedTaskHandled方法。
1 private void ensureQueuedTaskHandled(Runnable command) { 2 final ReentrantLock mainLock = this.mainLock; 3 mainLock.lock(); 4 boolean reject = false; 5 Thread t = null; 6 try { 7 int state = runState; 8 if (state != RUNNING && workQueue.remove(command)) 9 reject = true; 10 else if (state < STOP && 11 poolSize < Math.max(corePoolSize, 1) && 12 !workQueue.isEmpty()) 13 t = addThread(null); 14 } finally { 15 mainLock.unlock(); 16 } 17 if (reject) 18 reject(command); 19 else if (t != null) 20 t.start(); 21 }
ensureQueuedTaskHandled方法判斷線程池運行,如果狀態不為運行狀態,從workQueue中刪除, 並調用reject做拒絕處理。
1 void reject(Runnable command) { 2 handler.rejectedExecution(command, this); 3 }
再次回到execute方法代碼的5-10行。如線程池workQueue offer失敗或不處於運行狀態,調用addIfUnderMaximumPoolSize,addIfUnderMaximumPoolSize方法基本和addIfUnderCorePoolSize實現類似,不同點在於根據最大線程數(maximumPoolSize)進行比較,如果超過最大線程數,返回false,調用reject方法。
3.4 線程池中的線程初始化
默認情況下,創建線程池之后,線程池中是沒有線程的,需要提交任務之后才會創建線程。在實際中如果需要線程池創建之后立即創建線程,可以通過以下兩個方法辦到:
- prestartCoreThread():初始化一個核心線程;
- prestartAllCoreThreads():初始化所有核心線程
3.5 任務拒絕策略
當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會采取任務拒絕策略,通常有以下四種策略:
1 ThreadPoolExecutor.AbortPolicy //丟棄任務並拋出RejectedExecutionException異常。 2 ThreadPoolExecutor.DiscardPolicy //也是丟棄任務,但是不拋出異常。 3 ThreadPoolExecutor.DiscardOldestPolicy //丟棄隊列最前面的任務,然后重新嘗試執行任務(重復此過程) 4 ThreadPoolExecutor.CallerRunsPolicy //由調用線程處理該任務
3.6 線程池的關閉
ThreadPoolExecutor提供了兩個方法,用於線程池的關閉,分別是shutdown()和shutdownNow(),其中:
- shutdown():不會立即終止線程池,而是要等所有任務緩存隊列中的任務都執行完后才終止,但再也不會接受新的任務
- shutdownNow():立即終止線程池,並嘗試打斷正在執行的任務,並且清空任務緩存隊列,返回尚未執行的任務
四. 配置線程池的大小
一般需要根據任務的類型來配置線程池大小:
- 如果是CPU密集型任務,就需要盡量壓榨CPU,參考值可以設為 NCPU+1
- 如果是IO密集型任務,參考值可以設置為2*NCPU
當然,這只是一個參考值,具體的設置還需要根據實際情況進行調整,比如可以先將線程池大小設置為參考值,再觀察任務運行情況和系統負載、資源利用率來進行適當調整。
五. 示例
1 public class Test { 2 public static void main(String[] args) { 3 ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, 4 new ArrayBlockingQueue<Runnable>(5)); 5 6 for(int i=0;i<15;i++){ 7 MyTask myTask = new MyTask(i); 8 executor.execute(myTask); 9 System.out.println("線程池中線程數目:"+executor.getPoolSize()+",隊列中等待執行的任務數目:"+ 10 executor.getQueue().size()+",已執行完別的任務數目:"+executor.getCompletedTaskCount()); 11 } 12 executor.shutdown(); 13 } 14 } 15 16 17 class MyTask implements Runnable { 18 private int taskNum; 19 20 public MyTask(int num) { 21 this.taskNum = num; 22 } 23 24 @Override 25 public void run() { 26 System.out.println("正在執行task "+taskNum); 27 try { 28 Thread.currentThread().sleep(4000); 29 } catch (InterruptedException e) { 30 e.printStackTrace(); 31 } 32 System.out.println("task "+taskNum+"執行完畢"); 33 } 34 }
運行結果:

線程池中線程數目:1,隊列中等待執行的任務數目:0,已執行完別的任務數目:0 線程池中線程數目:2,隊列中等待執行的任務數目:0,已執行完別的任務數目:0 線程池中線程數目:3,隊列中等待執行的任務數目:0,已執行完別的任務數目:0 線程池中線程數目:4,隊列中等待執行的任務數目:0,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:0,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:1,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:2,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:3,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:4,已執行完別的任務數目:0 線程池中線程數目:5,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 線程池中線程數目:6,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 線程池中線程數目:7,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 線程池中線程數目:8,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 線程池中線程數目:9,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 線程池中線程數目:10,隊列中等待執行的任務數目:5,已執行完別的任務數目:0 正在執行task 1 正在執行task 3 正在執行task 10 正在執行task 12 正在執行task 14 正在執行task 0 正在執行task 2 正在執行task 4 正在執行task 11 正在執行task 13 task 1執行完畢 正在執行task 5 task 12執行完畢 正在執行task 6 task 10執行完畢 正在執行task 7 task 3執行完畢 正在執行task 8 task 14執行完畢 正在執行task 9 task 0執行完畢 task 2執行完畢 task 13執行完畢 task 4執行完畢 task 11執行完畢 task 5執行完畢 task 8執行完畢 task 7執行完畢 task 6執行完畢 task 9執行完畢
參考:http://www.cnblogs.com/dolphin0520/p/3932921.html
http://blog.csdn.net/java2000_wl/article/details/22097059