閱讀278 返回首頁    go 阿裏雲 go 技術社區[雲棲]


線程池

線程池(Thread Pool)對於限製應用程序中同一時刻運行的線程數很有用。因為每啟動一個新線程都會有相應的性能開銷,每個線程都需要給棧分配一些內存等等。

我們可以把並發執行的任務傳遞給一個線程池,來替代為每個並發執行的任務都啟動一個新的線程。隻要池裏有空閑的線程,任務就會分配給一個線程執行。在線程池的內部,任務被插入一個阻塞隊列(Blocking Queue ),線程池裏的線程會去取這個隊列裏的任務。當一個新任務插入隊列時,一個空閑線程就會成功的從隊列中取出任務並且執行它。

線程池經常應用在多線程服務器上。每個通過網絡到達服務器的連接都被包裝成一個任務並且傳遞給線程池。線程池的線程會並發的處理連接上的請求。以後會再深入有關 Java 實現多線程服務器的細節。

Java 5 在 java.util.concurrent 包中自帶了內置的線程池,所以你不用非得實現自己的線程池。你可以閱讀我寫的 java.util.concurrent.ExecutorService 的文章以了解更多有關內置線程池的知識。不過無論如何,知道一點關於線程池實現的知識總是有用的。

這裏有一個簡單的線程池實現:

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();
    }
  }

}

(校注:原文有編譯錯誤,我修改了下)

public class PoolThread extends Thread {

  private BlockingQueue<Runnable> taskQueue = null;
  private boolean       isStopped = false;

  public PoolThread(BlockingQueue<Runnable> queue) {
    taskQueue = queue;
  }

  public void run() {
    while (!isStopped()) {
      try {
        Runnable runnable =taskQueue.take();
        runnable.run();
      } catch(Exception e) {
        // 寫日誌或者報告異常,
        // 但保持線程池運行.
      }
    }
  }

  public synchronized void toStop() {
    isStopped = true;
    this.interrupt(); // 打斷池中線程的 dequeue() 調用.
  }

  public synchronized boolean isStopped() {
    return isStopped;
  }
}

線程池的實現由兩部分組成。類 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() 方法退出,子線程終止。

(校對注:看完覺得不過癮?更詳細的線程池文章參見Java線程池的分析和使用

下一節 : Anatomy of a Synchronizer


文章轉自 並發編程網-ifeve.com

最後更新:2017-05-22 20:04:38

  上一篇:go  多線程的優點
  下一篇:go  重入鎖死