2013-01-09 53 views
8

我想爲在線程池中執行的線程設置超時。目前,我有下面的代碼:Java:爲ThreadPool中的線程設置超時

ExecutorService executor = Executors.newFixedThreadPool(8); 
for(List<String> l: partition) {    
    Runnable worker = new WorkerThread(l); 
    executor.execute(worker); 
}  

executor.shutdown(); 
while (!executor.isTerminated()) { 

} 

代碼只是拆分對象的大名單變成子列表並處理根單絲中的這些子表。但這不是重點。

我想給線程池中的每個單線程超時。對於只有一個池線程我發現了以下解決方案:

Future<?> future = null; 

for (List<String> l : partition) { 
    Runnable worker = new WorkerThread(l); 
    future = executor.submit(worker); 
} 

try { 
    System.out.println("Started.."); 
    System.out.println(future.get(3, TimeUnit.SECONDS)); 
    System.out.println("Finished!"); 
} catch (TimeoutException e) { 
    System.out.println("Terminated!"); 
} 

,但這不會超過一個線程工作。也許我必須把每個線程放在一個List<Future>列表中,並遍歷這個列表併爲每個對象設置一個超時時間?

有什麼建議嗎?

編輯使用CountDownLatch AFTER:

CountDownLatch doneSignal = new CountDownLatch(partition.size()); 
List<Future<?>> tasks = new ArrayList<Future<?>>(); 
ExecutorService executor = Executors.newFixedThreadPool(8); 
for (List<String> l : partition) { 
    Runnable worker = new WorkerThread(l); 
    tasks.add(executor.submit(doneSignal, worker)); 
} 

doneSignal.await(1, TimeUnit.SECONDS); 
if (doneSignal.getCount() > 0) { 
    for (Future<?> fut : tasks) { 
    if (!fut.isDone()) { 
     System.out.println("Task " + fut + " has not finshed!"); 
     //fut.cancel(true) Maybe we can interrupt a thread this way?! 
    } 
    } 
} 

工作好爲止。

那麼下一個問題是如何中斷超時的線程?我嘗試fut.cancel(true)並添加在工作線程的一些關鍵迴路下面的結構:

if(Thread.interrupted()) { 
    System.out.println("!!Thread -> " + Thread.currentThread().getName() + " INTERRUPTED!!"); 
     return; 
} 

所以工作線程超時後「封殺」。這是一個好的解決方案嗎?

此外:是否有可能通過Future接口超時線程的名稱?目前,我必須在Thread.interrupted()結構的if條件中打印出名稱。

感謝您的幫助!

Regards

+1

是的,它適用於更多,只是將期貨保持在列表中。 – Fildor

+4

請不要用這種方式來思考線程。有些事情正在完成,如果需要超過一定的時間,則需要超時。這不是什麼線索可能發生在做這項工作,它是*工作*。也許兩個線程正在進行合作。當時可能沒有線程正在處理那項工作。但它是你想要停止或超時的*工作*,而不是可能或可能不會發生的線程或線程。這種細微的觀點轉變對正確思考線程非常重要,這樣您就可以收到良好的設計。 –

+1

@DavidSchwartz好點,但是可能有些作品是完全獨立於彼此的情況。我不知道OP是否確實是這樣,但在這裏看起來就是這樣。 – fge

回答

3

你見過? ExecutorService.invokeAll

它應該正是你想要的:調用一捆工作人員,並讓他們超時如果花費太長時間。評論後

編輯 - (新概念): 可以使用CountDownLatch等待任務,通過完成await(long timeout, TimeUnit unit)和超時! 然後,您可以連做一個shutdownNow時,看看哪些任務已花費的時間太長......

編輯2:

這樣可以很清楚:

  1. 擁有的CountDownLatch通過每個勞工進行倒計時, 等結束了。
  2. 在主執行線程await上所述鎖存器超時。
  3. 當該調用返回時,您可以檢查鎖存器的計數以查看是否存在超時命中(如果它> 0)。
  4. a)count = 0,所有任務都按時完成。 b)如果沒有,循環期貨並檢查其isDone。您不必在ExecutorService上調用關閉。
  5. 如果您不再需要執行程序,請致電關機。

注:工作人員可以在超時的同時完成,並呼籲他們未來的isDone()。

+0

好的......不過,如果線程完成了,我必須調用「executor.shutdown()」,對嗎? – sk2212

+0

不一定。您仍然可以在每個未來中循環調查isDone()或調用get(),或者您可以使用http://docs.oracle.com/javase/7/docs/api/java/util/concurrent /CountDownLatch.html我認爲後者是「最好」的方式。 – Fildor

+0

啊,好吧。 「CountDownLatch」看起來非常好。你說(在你的「編輯」行中)我可以在調用await之後運行shutdownNow。那麼shutdownNow方法會取消所有因設置超時而仍在運行的線程? 這是我的下一個問題,因爲我發現無法用invokeAll找出哪些線程「違反」超時。也許用新的方法是可能的? – sk2212