拾貝殼

          走過的路
          隨筆 - 39, 文章 - 1, 評論 - 14, 引用 - 0
          數(shù)據(jù)加載中……

          一個簡單的ThreadPool分析

          一個簡單的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,弄得初學者經(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

          posted on 2006-07-16 20:07 binge 閱讀(7188) 評論(1)  編輯  收藏 所屬分類: J2SE

          評論

          # re: 一個簡單的ThreadPool分析  回復(fù)  更多評論   

          糾正一個明顯的錯誤:

          ----------
          關(guān)于為什么要采用ThreadPool,原文已經(jīng)提到了:創(chuàng)建一個線程是需要開銷的,如果線程數(shù)量過大的話,cpu就會浪費很大的精力做線程切換。
          ----------

          線程池的目的不是reuse,而是overload shield.這是初涉多線程編程的人經(jīng)常會犯的概念性錯誤,也是對于線程池功能最常見的誤解

          況且,你指出的原文已經(jīng)說的很明白了,你似乎沒有理解原文的意思
          原文:
          Why a Thread Pool?
          When programming the crawler in the previous section, a problem that would soon present itself is the number of threads to use. A crawler may have to visit tens of thousands of pages, and you certainly do not want to create tens of thousands of threads because each thread imposes a certain amount of overhead. If the number of threads grows too large, the computer will be spending all of its time switching between threads, rather than just executing them.

          To solve this problem, you must create a thread pool. The thread pool is given some fixed number of threads to use. The thread pool will assign its tasks to each of these threads. As the threads finish with old tasks, new ones are assigned. This causes the program to use a fixed number of threads, not to be continually creating new threads.

          -----------------

          再說,這篇文章太老了,說java沒有提供內(nèi)建的線程池實現(xiàn),java5.0已經(jīng)提供了內(nèi)建的線程池
          2006-07-18 02:59 | fisher
          主站蜘蛛池模板: 天祝| 堆龙德庆县| 东丽区| 古交市| 黄平县| 张家川| 塔城市| 安图县| 阳谷县| 泰兴市| 沁源县| 连山| 胶南市| 横峰县| 大港区| 桑日县| 上饶县| 景东| 祁门县| 无极县| 宁明县| 克拉玛依市| 醴陵市| 离岛区| 苏尼特左旗| 禹城市| 合水县| 石楼县| 安徽省| 津市市| 洞口县| 商河县| 赤壁市| 林州市| 嘉祥县| 文成县| 陵川县| 印江| 黄冈市| 淮安市| 西城区|