本示例程序由三個類構成第一個是TestThreadPool類它是一個測試程序用來模擬客戶端的請求當你運行它時系統首先會顯示線程池的初始化信息然後提示你從鍵盤上輸入字符串並按下回車鍵這時你會發現屏幕上顯示信息告訴你某個線程正在處理你的請求如果你快速地輸入一行行字符串那麼你會發現線程池中不斷有線程被喚醒來處理你的請求在本例中我創建了一個擁有個線程的線程池如果線程池中沒有可用線程了系統會提示你相應的警告信息但如果你稍等片刻那你會發現屏幕上會陸陸續續提示有線程進入了睡眠狀態這時你又可以發送新的請求了
第二個類是ThreadPoolManager類顧名思義它是一個用於管理線程池的類它的主要職責是初始化線程池並為客戶端的請求分配不同的線程來進行處理如果線程池滿了它會對你發出警告信息
最後一個類是SimpleThread類它是Thread類的一個子類它才真正對客戶端的請求進行處理SimpleThread在示例程序初始化時都處於睡眠狀態但如果它接受到了ThreadPoolManager類發過來的調度信息則會將自己喚醒並對請求進行處理
首先我們來看一下TestThreadPool類的源碼
//TestThreadPooljava
import javaio*;
public class TestThreadPool
{
public static void main(String[] args)
{
try{
BufferedReader br = new BufferedReader(new InputStreamReader(Systemin));
String s;
ThreadPoolManager manager = new ThreadPoolManager();
while((s = brreadLine()) != null)
{
managerprocess(s);
}
}catch(IOException e){}
}
}
由於此測試程序用到了輸入輸入類因此第行導入了JAVA的基本IO處理包在第行中我們創建了一個名為manager的類它給ThreadPoolManager類的構造函數傳遞了一個值為的參數告訴ThreadPoolManager類我要一個有個線程的池給我創建一個吧!第行至行是一個無限循環它用來等待用戶的鍵入並將鍵入的字符串保存在s變量中並調用ThreadPoolManager類的process方法來將這個請求進行處理
下面我們再進一步跟蹤到ThreadPoolManager類中去以下是它的源代碼
//ThreadPoolManagerjava
import javautil*;
class ThreadPoolManager
{
private int maxThread;
public Vector vector;
public void setMaxThread(int threadCount)
{
maxThread = threadCount;
}
public ThreadPoolManager(int threadCount)
{
setMaxThread(threadCount);
Systemoutprintln(Starting thread pool);
vector = new Vector();
for(int i = ; i <= ; i++)
{
SimpleThread thread = new SimpleThread(i);
vectoraddElement(thread);
threadstart();
}
}
public void process(String argument)
{
int i;
for(i = ; i <vectorsize(); i++)
{
SimpleThread currentThread = (SimpleThread)vectorelementAt(i);
if(!currentThreadisRunning())
{
Systemoutprintln(Thread + (i+) + is processing: +
argument);
currentThreadsetArgument(argument);
currentThreadsetRunning(true);
return;
}
}
if(i == vectorsize())
{
Systemoutprintln(pool is full try in another time);
}
}
}//end of class ThreadPoolManager
我們先關注一下這個類的構造函數然後再看它的process()方法第-行是它的構造函數首先它給ThreadPoolManager類的成員變量maxThread賦值maxThread表示用於控制線程池中最大線程的數量第行初始化一個數組vector它用來存放所有的SimpleThread類這時候就充分體現了JAVA語言的優越性與藝術性如果你用C語言的話至少要寫行以上的代碼來完成vector的功能而且C語言數組只能容納類型統一的基本數據類型無法容納對象好了閒話少說第-行的循環完成這樣一個功能先創建一個新的SimpleThread類然後將它放入vector中去最後用threadstart()來啟動這個線程為什麼要用start()方法來啟動線程呢?因為這是JAVA語言中所規定的如果你不用的話那這些線程將永遠得不到激活從而導致本示例程序根本無法運行
下面我們再來看一下process()方法第-行的循環依次從vector數組中選取SimpleThread線程並檢查它是否處於激活狀態(所謂激活狀態是指此線程是否正在處理客戶端的請求)如果處於激活狀態的話那繼續查找vector數組的下一項如果vector數組中所有的線程都處於激活狀態的話那它會打印出一條信息提示用戶稍候再試相反如果找到了一個睡眠線程的話那第-行會對此進行處理它先告訴客戶端是哪一個線程來處理這個請求然後將客戶端的請求即字符串argument轉發給SimpleThread類的setArgument()方法進行處理並調用SimpleThread類的setRunning()方法來喚醒當前線程來對客戶端請求進行處理
可能你還對setRunning()方法是怎樣喚醒線程的有些不明白那我們現在就進入最後一個類SimpleThread類它的源代碼如下
//SimpleThreadjava
class SimpleThread extends Thread
{
private boolean runningFlag;
private String argument;
public boolean isRunning()
{
return runningFlag;
}
public synchronized void setRunning(boolean flag)
{
runningFlag = flag;
if(flag)
thisnotify();
}
public String getArgument()
{
return thisargument;
}
public void setArgument(String string)
{
argument = string;
}
public SimpleThread(int threadNumber)
{
runningFlag = false;
Systemoutprintln(thread + threadNumber + started);
}
public synchronized void run()
{
try{
while(true)
{
if(!runningFlag)
{
thiswait();
}
else
{
Systemoutprintln(processing + getArgument() + done);
sleep();
Systemoutprintln(Thread is sleeping);
setRunning(false);
}
}
} catch(InterruptedException e){
Systemoutprintln(Interrupt);
}
}//end of run()
}//end of class SimpleThread
如果你對JAVA的線程編程有些不太明白的話那我先在這裡簡單地講解一下JAVA有一個名為Thread的類如果你要創建一個線程則必須要從Thread類中繼承並且還要實現Thread類的run()接口要激活一個線程必須調用它的start()方法start()方法會自動調用run()接口因此用戶必須在run()接口中寫入自己的應用處理邏輯那麼我們怎麼來控制線程的睡眠與喚醒呢?其實很簡單JAVA語言為所有的對象都內置了wait()和notify()方法當一個線程調用wait()方法時則線程進入睡眠狀態就像停在了當前代碼上了也不會繼續執行它以下的代碼了當調用notify()方法時則會從調用wait()方法的那行代碼繼續執行以下的代碼這個過程有點像編譯器中的斷點調試的概念以本程序為例第行調用了wait()方法則這個線程就像凝固了一樣停在了行上了如果我們在第行進行一個notify()調用的話那線程會從第行上喚醒繼續從第行開始執行以下的代碼了
通過以上的講述我們現在就不難理解SimpleThread類了第-行通過設置一個標志runningFlag激活當前線程第-行是SimpleThread類的構造函數它用來告訴客戶端啟動的是第幾號進程第-行則是我實現的run()接口它實際上是一個無限循環在循環中首先判斷一下標志runningFlag如果沒有runningFlag為false的話那線程處理睡眠狀態否則第-行會進行真正的處理先打印用戶鍵入的字符串然後睡眠秒鐘為什麼要睡眠秒鐘呢?如果你不加上這句代碼的話由於計算機處理速度遠遠超過你的鍵盤輸入速度因此你看到的總是第號線程來處理你的請求從而達不到演示效果最後第行調用setRunning()方法又將線程置於睡眠狀態等待新請求的到來
最後還有一點要注意的是如果你在一個方法中調用了wait()和notify()函數那你一定要將此方法置為同步的即synchronized否則在編譯時會報錯並得到一個莫名其妙的消息current thread not owner(當前線程不是擁有者)
至此為止我們完整地實現了一個線程池當然這個線程池只是簡單地將客戶端輸入的字符串打印到了屏幕上而沒有做任何處理對於一個真正的企業級運用本例還是遠遠不夠的例如錯誤處理線程的動態調整性能優化臨界區的處理客戶端報文的定義等等都是值得考慮的問題但本文的目的僅僅只是讓你了解線程池的概念以及它的簡單實現如果你想成為這方面的高手本文是遠遠不夠的你應該參考一些更多的資料來深入地了解它
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26937.html