2011年11月24日

Java:超簡單 Thread Pool 功能實作


 
介紹 Thread Pool 之前,我們要先搞清楚 Thread 是什麼。
因為內容很多,所以這邊引用修改了 Program,Process,Thread 的資料:
  • Program:
    一群程式碼的集合,用以解決特定的問題。在物件導向中相當於類別(Class)。
  • Process:
    由Program所產生的執行個體,一個Program可以同時執行多次產生多個Process。在物件導向中相當於Object。
而 Process又由 Memory Space 和一個以上的 Thread 所組成,其中:
  • Memory Space:相當於Object的variable,不同Process的Memory Space也不同,彼此看不到其他Process的Memory Space。
  • 一個以上的Thread:
    Thread代表從某個起始點開始(例如main),到目前為止所有函數的呼叫路徑,以及這些呼叫路徑上所用到的區域變數。
當然程式的執行狀態,除了紀錄在主記憶體外,CPU內部的暫存器(如Program Counter, Stack Pointer, Program Status Word等)也需要一起紀錄。所以Thread又由下面兩項組成:
  • Stack:紀錄函數呼叫路徑,以及這些函數所用到的區域變數。
  • 目前CPU的狀態。
因此 Thread 的重點如下,一個Process可以有多個Thread。同一Process內的Thread使用相同的Memory Space,但這些Thread各自擁有其Stack。換句話說,Thread能透過reference存取到相同的Object,但是local variable卻是各自獨立的。作業系統會根據Thread的優先權以及已經用掉的CPU時間在不同的Thread作切換,以讓各個Thread都有機會執行。

嗯,好像有點扯太遠了,
總之Thread 執行緒常被定義為一個輕量的(Lightweight) Process,可用來處理 request,
然而雖然是輕量,但在產生 Thread 時仍會有 overhead,此時若 request 量太大,
又沒有善加管理這些 Thread 的話,就會拖累整體系統的效能。
為了解決這種類型的效能問題,所以就有了 Thread Pool 的概念產生。

Thread Pool 的概念如同其名,就是一個 Thread 的 Pool,
其中有固定或變動量的 Thread,當 request 進來時,若有閒置的 Thread 就執行,
若沒有的話,可能產生新的 Thread 或把 request 放入 queue 中等待被執行,
當一條 Thread 執行完工作而 queue 中仍有 request 在等待時,
此 Thread 應該要被分發新的 request 並處理。

由以上幾行,我們可以看出 Thread Pool 的工作有:
  1. 管控 Thread 的產生與回收
  2. 分發 Thread 處理 request
  3. 處理 request 的 queue
這三點其實很不簡單,
若有興趣的人可以參考Design Pattern: Thread Pool 模式
看看以前是怎麼實作 Thread Pool,以往用 Java 寫 Thread Pool 並不是簡單的事,
不過在 JavaSE 5.0 後,只要用簡單的幾行程式碼就可以做出 Thread Pool 的效果:
 1 package demo;
 2 
 3 import java.util.concurrent.Executor;
 4 import java.util.concurrent.Executors;
 5 
 6 public class ThreadPoolDemo {
 7 
 8     public static void main(String[] args) {
 9         
10         // 建立 2 個 thread 的 thread pool
11         Executor executor = Executors.newFixedThreadPool(2);  
12         
13         // 執行實作了 Runnable 介面的內部類別 Work
14         executor.execute(new Work(1));  
15         executor.execute(new Work(2));  
16         executor.execute(new Work(3));  
17 
18         // 直接在 function 中宣告匿名內部類別
19         executor.execute(new Runnable() {
20             // anonymous inner class            
21             @Override
22             public void run() {
23                 System.out.println(Thread.currentThread().getName() + 
24                     " Begins Work in anonymous inner class.");  
25             }
26         });
27     }
28     
29     public static class Work implements Runnable {  
30         private int id;  
31       
32         public Work (int id) {  
33             this.id = id;  
34         }  
35       
36         public void run() {  
37             System.out.println(Thread.currentThread().getName() + 
38                 " Begins Work " + id);  
39             try {  
40                 Thread.sleep(5000);  
41             }  
42             catch (InterruptedException ex) {  
43                 ex.printStackTrace();  
44             }  
45             System.out.println(Thread.currentThread().getName() + 
46                 " Ends Work " + id);  
47         }  
48     }  
49 }
執行結果如下:
pool-1-thread-2 Begins Work 2
pool-1-thread-1 Begins Work 1
pool-1-thread-1 Ends Work 1
pool-1-thread-1 Begins Work 3
pool-1-thread-2 Ends Work 2
pool-1-thread-2 Begins Work in anonymous inner class.
pool-1-thread-1 Ends Work 3
由以上的程式中我們可以發現,
Thread Pool 的 Thread 生命週期、request queue、分發request 都被 Java 做掉了,
我們所要做的就只有設定 Thread 的數量和專注在工作的內容,是不是相當簡單呢?

另外除了固定 Thread 數量的 Thread Pool 可用 Executors.newFixedThreadPool() 外,
Executors 也提供了其他的 method 來產生不同的 Thread Pool,如:
  1. newSingleThreadExecutor()
  2. newCachedThreadPool()
  3. newScheduledThreadPool()
  4. newSingleThreadScheduledExecutor() 等
若上面沒有符合自己需求的,
也可以參考 java.util.concurrent.ThreadPoolExecutor 建立符合自己需求的 Thread Pool。

關鍵字:Java, Thread, Pool, Executor, Executors, ThreadExecutor, ThreadPoolExecutor
參考資料:


更多精選推薦文章