2010-08-24 53 views
19

在Java 1.6(和來自Eclipse)中發現在Executors.newCachedThreadPool()中運行FutureTask吞下Runnable.run()方法中的異常後,我試圖想出一種方法來捕獲這些異常,而不添加throw/catch到我所有的Runnable實現。如何在FutureTask中捕獲異常

的API建議重寫FutureTask.setException()應有助於此:

原因這個未來與給定的throwable作爲其原因報告一個ExecutionException,除非未來已經設置或已被取消。這種方法在計算失敗時由run方法在內部調用。

但是這種方法似乎並沒有被調用(與調試器中運行顯示異常被FutureTask抓住了,但setException不叫)。我已經寫了以下程序來重現我的問題:

public class RunTest { 
    public static void main(String[] args) { 
     MyFutureTask t = new MyFutureTask(new Runnable() { 

      @Override 
      public void run() { 
       throw new RuntimeException("Unchecked exception"); 

      } 
     }); 

     ExecutorService service = Executors.newCachedThreadPool(); 
     service.submit(t); 
    } 
} 

public class MyFutureTask extends FutureTask<Object> { 

    public MyFutureTask(Runnable r) { 
     super(r, null); 
    } 

    @Override 
    protected void setException(Throwable t) { 
     super.setException(t); 
     System.out.println("Exception: " + t); 
    } 
} 

我的主要問題是:如何捕獲在FutureTask中引發的異常? setException爲什麼不被叫?

另外我想知道爲什麼Thread.UncaughtExceptionHandler機制不被FutureTask使用,是否有任何理由呢?

+1

''setException'很好用。我複製粘貼你的代碼,它的工作原理。 您可能還想在調用任務的get()方法時嘗試執行異常。 – Kru 2010-08-24 12:02:23

+0

使用Callable/Future組合的錯誤方法。 future.get()方法爲您提供包裝在ExecutionException中的異常。 – Bhushan 2014-10-16 08:59:17

回答

20

setException可能不是用於重寫,而是提供讓您將結果設置爲例外,如果需要的話。你想要做的是覆蓋done()方法,並得到結果:

public class MyFutureTask extends FutureTask<Object> { 

    public MyFutureTask(Runnable r) { 
     super(r, null); 
    } 

    @Override 
    protected void done() { 
     try { 
      if (!isCancelled()) get(); 
     } catch (ExecutionException e) { 
      // Exception occurred, deal with it 
      System.out.println("Exception: " + e.getCause()); 
     } catch (InterruptedException e) { 
      // Shouldn't happen, we're invoked when computation is finished 
      throw new AssertionError(e); 
     } 
    } 
} 
+0

這工作,謝謝。 – Thirler 2010-08-24 12:45:35

+0

我們如何從ExecutionException對象中提取狀態碼? – Min2 2015-05-18 14:29:10

+0

@ Min2如果你想引發ExecutionException的異常,你可以像在println語句中那樣使用'getCause()'。 – gustafc 2015-05-18 15:12:14

2

我看過FutureTask的源代碼,找不到setException正在調用的地方。
FutureTask.Sync(內部類FutureTask)有一個innerSetException方法,在運行方法拋出Throwable的情況下調用該方法。該方法也在setException中被調用。
所以它像javadoc的接縫不正確(或者很難理解...)。

+0

看了一些更多的緩慢太陽臭蟲數據庫後:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6464365 從2006年開始,它是已知的,仍然不是固定的(儘管它被稱爲固定) – Thirler 2010-08-24 12:00:43

+1

@ Thirler - 錯誤報告清楚地表明它在Java 7 **中被修復**。 – 2010-08-24 14:29:34

12

您是否嘗試過使用UncaughtExceptionHandler

  • 您需要實現UncaughtExceptionHandler接口。
  • 要爲池線程設置UncaughtExceptionHandler,請在Executor.newCachedThreadPool(ThreadFactory)調用中提供ThreadFactory
  • 您可以通過setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

所創建的線程設置的UncaughtExceptionHandler提交任務,ExecutorService.execute,因爲只有從execute提交的任務拋出的異常使其未捕獲的異常處理程序。對於使用ExecutorService.submit提交的任務,任何拋出的異常都被認爲是任務返回值的一部分。如果與提交提交一項任務異常終止,則調用Future.get當被再次拋出,包裹在一個ExecutionException

+0

好抓!我錯誤地使用了'ExecutorService.submit'。 – Gili 2011-09-05 15:38:04

8

更好的解決方案: Java FutureTask completion check

當你打電話futureTask.get()獲取計算的結果,它會如果底層Runnable/Callable引發異常,則拋出異常(ExecutionException)。

ExecutionException.getCause()將返回異常,即Runnable/Callable丟棄。

如果Runnable/Callable被取消,它也會拋出不同的異常。