一個簡單的ThreadPool
? 原文來自
http://www.informit.com/articles/printerfriendly.asp?p=30483&r1=1&rl=1? 項目是多線程的,所以引入了線程池這個東西。池子是個老美寫的。在項目中表現(xiàn)的還不錯。所以把它摘出來,介紹給以后或許需要用到它的同行們。
? 關(guān)于為什么要采用ThreadPool,原文已經(jīng)提到了:創(chuàng)建一個線程是需要開銷的;如果線程數(shù)量過大的話,cpu就會浪費很大的精力做線程切換。
? ThreadPool的實現(xiàn)過程就是對WorkerThread的同步和通信的管理過程。
? 我們來看代碼。
? 首先,在ThreadPool構(gòu)造的時候,創(chuàng)建10個WorkerThread(size=10)并讓他們運行。每個WorkerThread線程都有個ThreadPool的引用,用于查詢ThreadPool的狀態(tài)和獲得同步鎖.WorkerThread運行以后,循環(huán)調(diào)用ThreadPool的方法進行查詢,如果沒有發(fā)現(xiàn)任務(wù),ThreadPool告訴正在查詢的線程進入休眠狀態(tài),WorkerThread釋放對查詢方法的鎖定.這樣在還沒有任務(wù)的時候,所有的10個WorkerThread都會進入休眠狀態(tài),進入等待ThreadPool對象的等待鎖定池,只有ThreadPool對象發(fā)出notify方法(或notifyAll)后WorkerThread線程才進入對象鎖定池準備獲得對象鎖進入運行狀態(tài)。
代碼片斷:
while ( !assignments.iterator().hasNext() )
??? wait();
如果你有jprofile或者其他的觀察線程的工具,你可以看到有10個線程都在休眠狀態(tài).
? 接著,我們向ThreadPool中加入任務(wù),這些任務(wù)都實現(xiàn)了Runnable的run方法.(至于為什么把任務(wù)都做成Runnable,譯者至今也有些疑問?預(yù)定俗成?TimerTask也是實現(xiàn)自Runnable,弄得初學(xué)者經(jīng)常把真正運行的線程搞混).ThreadPool每assign一個任務(wù),就會發(fā)出一條消息,通知它的等待鎖定池中的線程.各個線程以搶占的方式獲得對象鎖,然后很順利的獲得一條任務(wù).并把此任務(wù)從ThreadPool里面刪除.沒有搶到的繼續(xù)等待.
Runnable r = (Runnable)assignments.iterator().next();
?? assignments.remove(r);
WorkerThread從ThreadPool那里獲得了任務(wù),繼續(xù)向下執(zhí)行。
target = owner.getAssignment();
?? if (target!=null) {
??? target.run();?????
??? owner.done.workerEnd();
?? }
記住,這里調(diào)用的是target.run();而不是調(diào)用的線程的start()方法。也就是說在這里表現(xiàn)出的WorkerThread和task之間的關(guān)系僅僅是簡單的方法調(diào)用的關(guān)系,并沒有額外產(chǎn)生新線程。(這就是我上面納悶為什么大家都實現(xiàn)Runnable來做task的原因)
?大家可能注意到,WorkerThread并沒有對異常作處理。而我們知道發(fā)生在線程上的異常會導(dǎo)致線程死亡。解決的辦法有2中,一種是通過threadpool的管理來重新激起一個線程,一種是把異常在線程之內(nèi)消滅。在項目中,我采用的是第二中,因此這個片斷改稱這樣:
if (target!=null) {
? try{
??? target.run();?????
?? }
? catch(Throwable t){
?.......
?? }
??? owner.done.workerEnd();
}
在WorkerThread完成一個task以后,繼續(xù)循環(huán)作同樣的流程.
在這個ThreadPool的實現(xiàn)里面,Jeff Heaton用了一個Done類來觀察WorkerThread的執(zhí)行情況.和ThreadPoool的等待鎖定池不同,Done的等待鎖定池里面放的是初始化ThreadPool的線程(可能是你的主線程),我們叫他母線程.
? 在給出的測試例子中.母線程在調(diào)用complete()方法后進入休眠(在監(jiān)視中等待),一開始是waitBegin()讓他休眠,在assign加入task以后,waitDone()方法讓他休眠.在WorkerThread完成一個task以后,通知waitDone()起來重新檢查activeThreads的數(shù)值.若不為0,繼續(xù)睡覺.若為0,那么母線程走完,死亡(這個時候該做的task已經(jīng)做完了).母線程走完,ThreadPool還存在嗎?答案是存在,因為WorkerThread還沒有消亡,他們在等待下一批任務(wù),他們有ThreadPool的引用,保證ThreadPool依然存在.大家或許已經(jīng)明白Done這個類的作用了.
? 細心的讀者或許會發(fā)現(xiàn),發(fā)生在Done實例上的notify()并不是像ThreadPool上的notify()那樣每次都能完成一項工作.比如除了第一個被assign的task,其他的task在assign進去的時候,發(fā)出的notify()對于waitDone()來說是句"狼來了".
?最后在ThreadPool需要被清理得時候,使每一個WorkerThread中斷(這個時候或許所有的WorkerThread都在休眠)并銷毀.記住這里也是一個異步的過程.等到每一個WorkerThread都已經(jīng)銷毀,finalize()的方法體走完.ThreadPool被銷毀.
?for (int i=0;i<threads.length;i++) {
?? threads[i].interrupt();
?? done.workerBegin();
?? threads[i].destroy();
? }
? done.waitDone();
為什么有句done.workerBegin();?不明白.
參考文章:
http://www.zdnet.com.cn/developer/common/printfriend/printfriendly.htm?AT=39276905-3800066897t-20000560c