線程池(Thread Pool)對於限制應用程序中同一時刻運行的線程數很有用。因為每啟動一個新線程都會有相應的性能開銷,每個線程都需要給棧分配一些內存等等。
我們可以把並發執行的任務傳遞給一個線程池,來替代為每個並發執行的任務都啟動一個新的線程。只要池裡有空閒的線程,任務就會分配給一個線程執行。在線程池的內部,任務被插入一個阻塞隊列(Blocking Queue),線程池裡的線程會去取這個隊列裡的任務。當一個新任務插入隊列時,一個空閒線程就會成功的從隊列中取出任務並且執行它。
線程池經常應用在多線程服務器上。每個通過網絡到達服務器的連接都被包裝成一個任務並且傳遞給線程池。線程池的線程會並發的處理連接上的請求。以後會再深入有關 Java 實現多線程服務器的細節。
Java 5 在 java.util.concurrent 包中自帶了內置的線程池,所以你不用非得實現自己的線程池。
public class ThreadPool { private BlockingQueue taskQueue = null; private List<PoolThread> threads = new ArrayList<PoolThread>(); private boolean isStopped = false; public ThreadPool(int noOfThreads, int maxNoOfTasks) { taskQueue = new BlockingQueue(maxNoOfTasks); for (int i=0; i<noOfThreads; i++) { threads.add(new PoolThread(taskQueue)); } for (PoolThread thread : threads) { thread.start(); } } public void synchronized execute(Runnable task) { if(this.isStopped) throw new IllegalStateException("ThreadPool is stopped"); this.taskQueue.enqueue(task); } public synchronized boolean stop() { this.isStopped = true; for (PoolThread thread : threads) { thread.stop(); } } }
線程池的實現由兩部分組成。類 ThreadPool 是線程池的公開接口,而類 PoolThread 用來實現執行任務的子線程。
為了執行一個任務,方法 ThreadPool.execute(Runnable r)用 Runnable 的實現作為調用參數。在內部,Runnable 對象被放入阻塞隊列 (Blocking Queue) ,等待著被子線程取出隊列。
一個空閒的 PoolThread 線程會把 Runnable 對象從隊列中取出並執行。你可以在 PoolThread.run()方法裡看到這些代碼。執行完畢後,PoolThread 進入循環並且嘗試從隊列中再取出一個任務,直到線程終止。
調用 ThreadPool.stop()方法可以停止 ThreadPool。在內部,調用 stop 先會標記 isStopped 成員變量(為 true)。然後,線程池的每一個子線程都調用 PoolThread.stop()方法停止運行。注意,如果線程池的 execute()在 stop()之後調用,execute()方法會拋出 IllegalStateException 異常。
子線程會在完成當前執行的任務後停止。注意 PoolThread.stop() 方法中調用了 this.interrupt()。它確保阻塞在 taskQueue.dequeue() 裡的 wait()調用的線程能夠跳出 wait()調用(校對注:因為執行了中斷 interrupt,它能夠打斷這個調用),並且拋出一個 InterruptedException 異常離開 dequeue()方法。這個異常在 PoolThread.run()方法中被截獲、報告,然後再檢查 isStopped 變量。由於 isStopped 的值是 true, 因此 PoolThread.run()方法退出,子線程終止。