2015-03-13 176 views
0

我有這種奇怪的行爲。儘管我設定了強制終止它的時間,但我的程序仍然掛起。任何人都可以指出這種奇怪的行爲可能是什麼?程序掛起,線程不會終止

這裏是我的代碼,我開始線程

protected void pendingTaskStarter() throws Exception { 
    ExecutorService service = Executors.newFixedThreadPool(maxThreadNum); 
    ArrayList<Future<Runnable>> futures = new ArrayList<Future<Runnable>>(); 

    System.out.println("max thread num: " + maxThreadNum); 
    for(int i=0;i<maxThreadNum;i++){ 

     Thread t = new PendingTaskConsumer(); 
     Future<?> f=service.submit(t); 
     futures.add((Future<Runnable>) f); 
    } 

    for (Future<?> future:futures) { 
     future.get(5l,TimeUnit.MINUTES); // maximum thread's life is 5min (5l <-- 5 in long) 
    } 
    service.shutdownNow(); 

} 

我100%肯定,我的計劃基於PendingTaskConsumer類中的輸出在PendingTaskConsumer類的地方掛起。

無論如何代碼PendingTaskConsumer應該是無關的,因爲線程被認爲是被迫終止。 我的問題是在什麼情景下,下面的行不按預期做。

future.get(5l,TimeUnit.MINUTES); 

該程序在Linux(Ubuntu)運行並使用openjdk-7-jdk backage (version 1.7)

+0

程序預計運行多長時間?你等了多久才能完成? – 2015-03-13 13:33:27

+0

我在這層上面有一個API,如果這個程序在20分鐘內沒有終止,那麼它會重啓運行這個程序的機器 – nafas 2015-03-13 13:34:39

+0

(1)爲什麼'PendingTaskConsumer'從'Thread'派生?它不是線程,而是由'ExecutorService'維護的ANOTHER線程執行的任務。 (2)是PendingTaskConsumer()方法調用?如果不是,則probalby缺少'new'關鍵字。 – isnot2bad 2015-03-13 20:12:16

回答

1

由於@ GPI已經在他/她的回答中聲明,如果您的任務沒有通過Thread.interrupt()響應線程中斷,取消將無法正常工作。見Oracle: The Java™ Tutorials - Concurrency - InterruptsJava Executors: how can I stop submitted tasks?

但是,即使您的任務不立即停止,如果線程中斷(=中斷標誌設置),您可以超時您的方法。只要不要通過調用Future的每一個get()方法來完成任務完成!這不是必要的,因爲你對他們的結果並不感興趣。只需關閉您的ExecutorService並等待其終止。

protected void pendingTaskStarter() throws InterruptedException { 
    ExecutorService service = Executors.newFixedThreadPool(maxThreadNum); 

    for (int i = 0; i < maxThreadNum; i++) { 
     service.submit(new PendingTaskConsumer()); 
    } 

    // Shutdown service. 
    // This will continue to process already submitted tasks. 
    service.shutdown(); 

    try { 
     // wait at least 5 minutes for the tasks to complete 
     if (!service.awaitTermination(5, TimeUnit.MINUTES)) { 
      // still not done yet -> force shutdown 
      // this will interrupt currently running tasks. 
      service.shutdownNow(); 
     } 
    } catch (InterruptedException ex) { 
     // someone interrupted this thread. Shutdown tasks immediately 
     service.shutdownNow(); 
     // propagate exception 
     throw ex; 
    } 
} 

那麼這裏會發生什麼?

  • 首先,提交所有任務。
  • 然後在ExecutorService上調用shutdown()。請注意該服務將continue to execute already submitted tasks, but it will not accept new tasks
  • 現在,我們等待服務終止,呼叫awaitTermination超時5分鐘。如果此方法返回true,這意味着服務已經在時間內終止,一切都很好,我們就完成了。但是如果5分鐘後它返回false,這意味着服務仍在運行,因爲有一些任務需要更多時間才能完成。
  • 在這種情況下,我們立即通過調用shutdownNow()來關閉服務。請注意,這種方法不會阻止!它只是試圖取消所有正在運行的任務,通常是通過中斷正在執行它們的線程。 這正是爲什麼你的任務應該正確響應線程中斷的原因!如果他們不這樣做,中斷線程根本就沒有任何作用!
  • 無論shutdownNow成功 - 我們完成了,不再等待。所以可能還有一些任務正在運行,因爲它們不會在線程中斷時作出反應(通常是因爲某個任務未正確實現,或者是故意不中斷)。但我們根本不在乎。
  • 請注意異常處理程序:當另一個線程在方法awaitTermination中等待時正在調用我們的方法的線程中斷時,將引發InterruptedException。我們通過關閉服務並重新拋出異常來處理它!這是一件好事,並使我們的方法成爲可以自行取消的長時間運行操作!
+0

這是一個非常明確的解釋。我喜歡你提到的部分,沒有關注未來的點。我會試試看,並讓你知道 – nafas 2015-03-16 12:27:03

+0

好吧,我會很驚訝,它做到了。我相信**拋出前者; **取得了訣竅。即使線程沒有終止。我擺脫了這種方法。那麼一旦執行了所有其他操作,我需要添加*** System.exit(0); ***強制退出程序。 – nafas 2015-03-16 13:25:01

+0

@nafas正如我所提到的,無論任務是否運行,該方法都會在至少5分鐘後終止。在你的情況下,它似乎仍然在運行 - 這就是爲什麼沒有'System.exit(0)',系統不會停止!你應該真的檢查你的任務實現並確保線程中斷處理正確! – isnot2bad 2015-03-16 15:28:30

1

嗯......適當的異常處理似乎缺少。

/** 
* Waits if necessary for at most the given time for the computation 
* to complete, and then retrieves its result, if available. 
* 
* @param timeout the maximum time to wait 
* @param unit the time unit of the timeout argument 
* @return the computed result 
* @throws CancellationException if the computation was cancelled 
* @throws ExecutionException if the computation threw an 
* exception 
* @throws InterruptedException if the current thread was interrupted 
* while waiting 
* @throws **TimeoutException if the wait timed out** 
*/ 
V get(long timeout, TimeUnit unit) 
    throws InterruptedException, ExecutionException, TimeoutException; 

如果您對future.get(5l,TimeUnit.MINUTES);期權到期,那麼Exception被拋出,你的service永遠不會關閉。

所以(可以假設)它的內部線程仍在運行,至少是執行長時間運行任務的線程)。看到這些是非守護進程線程(你必須定製你的執行程序的ThreadFactory),那些未決線程阻止JVM關閉。

如果要強行終止任務,您可以:當它看到成爲真正的

  1. 設計您運行的任務,以應對Thread.isInterrupted(),並退出。 (shutdownNow的實際效果是設置executor的線程的中斷標誌)
  2. 如果你不能(因爲你的某個庫的方法阻止了不聽中斷),那麼看起來有一個dameon線程像唯一的選擇。

但無論如何,我會把executor的提前關機,或者在finally條款中關機。

+0

非常有趣的隊友,我從來沒有考慮過這個。但我會嘗試並讓你知道。 – nafas 2015-03-13 13:39:05