2010-10-06 31 views
38

我有一個ScheduledThreadPoolExecutor,它似乎在吃異常。如果提交的Runnable引發異常,我希望執行程序服務通知我。ThreadPools中的異常處理

例如,我想下面的代碼起碼打印IndexArrayOutOfBoundsException的堆棧跟蹤

threadPool.scheduleAtFixedRate(
    new Runnable() { 
    public void run() { 
     int[] array = new array[0]; 
     array[42] = 5; 
    } 
    }, 
    1000, 
    1500L, 
    TimeUnit.MILLISECONDS); 

作爲一個方面的問題。有沒有辦法爲ScheduledThreadPoolExecutor編寫一個通用的try catch塊?原來問題的

////////// END //////////////

由於提出了下列裝飾效果很好。

public class CatcherTask implements Runnable{ 

    Runnable runMe; 

    public CatcherTask(Runnable runMe) { 
     this.runMe = runMe; 
    } 

    public void run() { 
     try { 
      runMe.run(); 
     } catch (Exception ex){ 
      ex.printStackTrace(); 
     } 
    } 
} 
+3

優秀的問題。我想知道爲什麼只有少數人遇到這種情況。 – whiskeysierra 2010-10-06 19:06:06

+0

另請參閱http://stackoverflow.com/questions/1687977/how-to-properly-catch-runtimeexceptions-from-executors – Raedwald 2014-04-29 15:57:33

回答

22

我寫了一個關於這個問題的小post前一陣子。你有兩個選擇:

  1. 使用由科林·赫伯特提供的solution
  2. 使用馬克彼得斯solution的修改版本,但不是分配UncaughtExceptionHandler你包裝每個已提交的Runnable到一個可運行你自己的,其執行(調用run)真正可以在try-catch-block內運行。

編輯
正如指出的馬克,包住Runnable傳遞給ScheduledExecutorService,而不是一個傳遞給ThreadFactory是很重要的。

+0

好吧,我確認並刪除了我的答案。謝謝!隨意修改您的第2步以瞭解詳情。 – 2010-10-06 19:10:48

+2

其實我試過了#2,並且無法使它工作。異常被捕獲到委託Runnable中的try/catch塊上方,所以這似乎屈服於與UncaughtExceptionHandler相同的問題。 – 2010-10-06 19:19:07

+0

我剛剛接受了這個 - 但馬克說,用try/catch裝飾提交的任務不是....我會試試這個。 – Ivan 2010-10-06 19:23:16

12

警告:此方法不適用於計劃線程池的執行者。這個答案因其與其他線程池執行者的相關性而被取消了。請參閱Willi's answer

覆蓋的ThreadFactory給線程的UncaughtExceptionHandler的:

ThreadPoolExecutor exec = new ThreadPoolExecutor...; 

exec.setThreadFactory(new ExceptionCatchingThreadFactory(exec.getThreadFactory())); 
//go on to submit tasks... 


private static class ExceptionCatchingThreadFactory implements ThreadFactory { 
    private final ThreadFactory delegate; 

    private ExceptionCatchingThreadFactory(ThreadFactory delegate) { 
     this.delegate = delegate; 
    } 

    public Thread newThread(final Runnable r) { 
     Thread t = delegate.newThread(r); 
     t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { 
      @Override 
      public void uncaughtException(Thread t, Throwable e) { 
       e.printStackTrace(); //replace with your handling logic. 
      } 
     }); 
     return t; 
    } 
} 
+4

'UncaughExceptionHandler'不適用於計劃的可運行參數。 – whiskeysierra 2010-10-06 18:54:10

+0

@Willi:讓我驗證一下,如果我確認,我會刪除它。 – 2010-10-06 19:01:00

+1

取消刪除,因爲它對於非預定線程池執行程序似乎仍然有用。 – 2010-10-06 19:26:10

0

考慮您的的ScheduledThreadPoolExecutor添加靜態事件類,如果有異常拋出您的任何任務都可以調用。這樣,您可以利用該事件來捕獲並處理線程中發生的異常。

+1

'ScheduledThreadPoolExecutor'在'java.util.concurrent'中...... – whiskeysierra 2010-10-06 19:01:37

5

您可以使用Future中的get()方法調用scheduleAtFixedRate()。如果在執行線程期間發生異常,它會拋出一個ExecutionException

+0

這對於後續調用是如何工作的?例如在第一次成功返回之後,你如何獲得第二次調用的未來? – 2010-10-06 18:47:38

+0

@Mark如果一次執行失敗,則不會執行第二次(或第三次,......)執行。從原始javadoc複製:*如果任務的任何執行遇到異常,則後續執行被抑制* – whiskeysierra 2010-10-06 19:04:26

+0

@Willi:那麼我想我對OP的陳述「似乎在吃例外......」感到困惑,如果它是隻運行一次。 – 2010-10-06 19:08:39

2

您也可以使用Spring Framework中的ThreadPoolTaskScheduler,它提供了一個設置錯誤處理程序的方法併爲您執行所有打包。默認行爲取決於任務的類型:

如果提供的ErrorHandler不爲空,它將被使用。否則,默認情況下,重複任務的錯誤被抑制,而一次性任務的默認錯誤會傳播,因爲通過返回Future可能會出現這些錯誤。在這兩種情況下,都會記錄錯誤。

如果你只想使用包裝一部分,而不是TaskScheduler可以使用

TaskUtils.decorateTaskWithErrorHandler(task, errorHandler, isRepeatingTask) 

其中TaskScheduler內部使用。

2

您可以對ScheduledThreadPoolExecutor進行子類化,並覆蓋afterExecute方法來處理您提交的任何類型的Runnable的異常和錯誤。