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