2010-02-24 38 views
49

我一直都在讀創建線程很昂貴。
我也知道你不能重新運行一個線程。java線程重用

我看到Executors類的文檔:

創建一個可根據需要創建新線程的線程池,但可用時將重用以前構造的線程。

注意「重用」一詞。

線程池如何重用線程?

回答

44

我想我明白了什麼是confuzzabling你,這裏是我的長回答:術語是一點點誤導性(顯然,不然你就不會問這個問題特別是將重點放在「重用」):

線程池如何重用線程?

正在發生的事情是,一個單獨的線程可以用於處理多個任務(通常作爲Runnable過去了,但是這取決於你的「執行人」框架:默認執行人接受Runnable,但你可以寫自己的「遺囑執行人「/ thread-pool接受比Runnable [比如說CancellableRunnable]更復雜的東西)。

現在在缺省ExecutorService實現中,如果某個線程在仍處於使用狀態時以某種方式終止,它將自動替換爲新線程,但這不是他們正在討論的「重用」。在這種情況下沒有「重用」。

所以這是事實,你不能在一個Java線程調用start()兩次只要你想執行人可以傳遞儘可能多的Runnable和各Runnablerun()方法應被調用一次。

可以傳遞30 Runnable至5的Java Thread和每個工作線程可調用,例如,run() 6倍(實際上還有的不能保證你會被執行剛好6 RunnableThread但是這是一個細節)。

在這個例子中start()將被調用6次。每個人這6 start()將調用恰好一次run()方法各Thread的:

Thread.start()的Javadoc:

* Causes this thread to begin execution; the Java Virtual Machine 
* calls the <code>run</code> method of this thread. 

然後裏面每個線程的run()方法Runnable將離隊並且將調用每個Runnablerun()方法。所以每個線程可以處理幾個Runnable。這就是他們所說的「線程重用」。

一種方式來執行自己的線程池是使用阻塞隊列上您排隊可運行,並有你的線程的每一個,一旦完成處理Runnablerun()方法,出隊的下一個Runnable(或塊)並運行其run()方法,然後沖洗並重復。

我猜混亂的一部分(這是一個有點混亂)來自這樣一個事實:Thread需要Runnable,並在調用時start() Runnable的run()方法被調用,而默認的線程池採取Runnable

+22

tl; dr線程池線程基本上是運行循環,將提交的任務從隊列中拉出。線程在服務任務時不會停止執行,而是等待下一個提交到隊列中。在問題中,他們從來沒有得到'重新運行',因爲他們只是不斷運行。 – Sogger 2013-02-15 20:16:38

4

線程池由許多固定的工作線程組成,可以從內部任務隊列中獲取任務。因此,如果一個任務結束,線程會執行而不是結束,但會等待下一個任務。如果您中止一個線程,它會自動替換。

查看documentation瞭解更多詳情。

-4

如果thread完成,確定可以再次使用它。你可以通過調用isAlive()或類似的東西來確定它沒有運行。

編輯:如果有人暫停thread,則無法重新開始。如果它正常結束,我不會爲什麼不能開始。但是我給出了懷疑的好處,並說它不能被重複使用。

+4

@fastcodejava:如果一個線程完成,你不能再次調用* start()*,那會給你一個* IllegalThreadStateException *。 – SyntaxT3rr0r 2010-02-24 06:40:05

12

run線程池中的線程方法不僅僅由運行單個任務組成。線程池中線程的run方法包含一個循環。它將任務從隊列中取出,執行任務(在完成時返回回到),然後獲取下一個任務。直到線程不再需要時,run方法纔會完成。

編輯補充:

這裏是ThreadPoolExecutorWorker內部類的run方法。

696:   /** 
697:   * Main run loop 
698:   */ 
699:   public void run() { 
700:    try { 
701:     Runnable task = firstTask; 
702:     firstTask = null; 
703:     while (task != null || (task = getTask()) != null) { 
704:      runTask(task); 
705:      task = null; // unnecessary but can help GC 
706:     } 
707:    } finally { 
708:     workerDone(this); 
709:    } 
710:   } 
+0

@Mike Daniels:確切地說...那就是說,你必須愛那個分配*「task = null;」的Sun代碼*,然後註釋*「//不必要但是可以幫助GC」*。你會在這裏發佈一段代碼,因爲這樣做,你可能會被羣體小夥子扯到你的喉嚨,告訴你這是多麼毫無意義;) – SyntaxT3rr0r 2010-02-24 06:43:05

+5

@Wizard:這實際上沒有必要 - 任務的(奇怪)測試!=循環中的null使得有必要防止連續處理相同的任務。即使循環是更傳統的歸零任務也是好的,否則如果getTask()阻塞很長時間,任務的GC將被延遲相同的時間長度。 – 2010-02-24 07:02:26

+0

@軟件猴:對第二點非常有見地。 'getTask'可能會超時阻塞隊列。這可能會阻塞超時的全部時間。如果任務有很多字段(對於傳遞給任務的參數),早期的GC會很有幫助。 – 2010-02-24 07:06:00

0

線程池創建自己的線程併爲這些線程提供自己聰明的小Runnables。這些Runnables永遠不會結束,而是在一個隊列上進行同步(它們將等待()),直到該隊列中存在一個Callable。他們會在發生這種情況時得到通知,並且他們的Runnable會從隊列中運行Callable,並且整個場景會再次重複。