2016-09-28 68 views
3

我正在開發一個多線程項目,其中Thread可能會拋出Error(而不是Exception)。沒有找到任何關於多線程中如何處理錯誤的可靠信息,我決定做一些測試並發現結果可能不一致。Java如何處理多線程中的錯誤?

這是我的測試代碼,以及評論結果。

public class MultiThreadError { 
    public static class ErrorThrowingRunnable implements Runnable{ 
     private final boolean throwsError; 
     public ErrorThrowingRunnable(boolean throwsError){ 
      this.throwsError = throwsError; 
     } 

     @Override 
     public void run() { 
      try { 
       // Wait between .5 and 1.5 seconds 
       Thread.sleep(500 + new Random().nextInt(1000)); 
      } catch (InterruptedException ex) {} 
      if(throwsError){ 
       throw new Error(Thread.currentThread().getName()); 
      }else{ 
       System.out.println(Thread.currentThread().getName()); 
      } 
     } 
    } 

    public static void regularThreadPool(){ 
     // Crashes individual thread; swallows error 
     ExecutorService threadPool = Executors.newFixedThreadPool(5); 
     threadPool.submit(new ErrorThrowingRunnable(false)); 
     threadPool.submit(new ErrorThrowingRunnable(false)); 
     threadPool.submit(new ErrorThrowingRunnable(false)); 
     threadPool.submit(new ErrorThrowingRunnable(false)); 
     threadPool.submit(new ErrorThrowingRunnable(true)); 
     threadPool.shutdown(); 
    } 

    public static void onDemandThreads(){ 
     // Crashes individual thread; displays error 
     new Thread(new ErrorThrowingRunnable(false)).start(); 
     new Thread(new ErrorThrowingRunnable(false)).start(); 
     new Thread(new ErrorThrowingRunnable(false)).start(); 
     new Thread(new ErrorThrowingRunnable(false)).start(); 
     new Thread(new ErrorThrowingRunnable(true)).start(); 
    } 

    public static void onDemandThreadPool(){ 
     // Same as onDemandThreads() 
     ExecutorService threadPool = Executors.newFixedThreadPool(5); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(true)); 
     threadPool.shutdown(); 
    } 

    public static void tooSmallThreadPool(){ 
     // When an error is thrown, apparently the thread that threw 
     // the error is not reused, reducing the pool size 
     ExecutorService threadPool = Executors.newFixedThreadPool(3); 
     threadPool.execute(new ErrorThrowingRunnable(true)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.execute(new ErrorThrowingRunnable(false)); 
     threadPool.shutdown(); 
    } 
} 

看起來好像結果應該是我的預期:拋出錯誤的線程終止,顯示消息。事實證明,當一個Runnable傳遞給ExecutorService使用submit(Runnable)時,它被封裝在RunnableFuture<Void>不處理錯誤,我找不到方法來改變這種行爲,而不是直接調用execute(Runnable),由於某種原因doesn沒有表現出相同的行爲。

對此有沒有「最佳實踐」?如果我知道一個線程可能會拋出一個錯誤,是否有一種方法來submit它到一個ExecutorService而不是吞下該錯誤?

+0

在小樣式的筆記中,您應該避免拋出錯誤。它們表明嚴重的問題,您不應該嘗試處理(例如內存不足)。如果你想從'Runnable'中拋出一些東西,你應該使用'RuntimeException'(或其子類)。 –

+0

[處理來自Java ExecutorService任務的異常]可能的重複(http://stackoverflow.com/questions/2248131/handling-exceptions-from-java-executorservice-tasks) –

+1

只是爲了確保,'RunnableFuture'_handlers_錯誤正好。所有可運行的調用都被封裝在try/finally中。你的意思是說你看不到我懷疑的錯誤信息。 – Gray

回答

5

是的,將您的任務提交到ExecutorService並檢查返回Future的結果。

使用時:

ExecutorService es = Executors.newFixedThreadPool(1); 

Future<?> result = es.submit(new Runnable() { 
    @Override 
    public void run() { 
     throw new Error("sample error"); 
    } 
}); 

try { 
    result.get(); 
} catch (ExecutionException e) { 
    e.printStackTrace(); 
} 

您的堆棧跟蹤將包含:

java.util.concurrent.ExecutionException: java.lang.Error: sample error 
    at java.util.concurrent.FutureTask.report(Unknown Source) 
    at java.util.concurrent.FutureTask.get(Unknown Source) 
    at jjj.b.B.main(B.java:23) 
Caused by: java.lang.Error: sample error 
    at jjj.b.B$1.call(B.java:18) 
    at jjj.b.B$1.call(B.java:1) 
    at java.util.concurrent.FutureTask.run(Unknown Source) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) 
    at java.lang.Thread.run(Unknown Source) 
+0

你確定一個錯誤將被捕獲,如果它說它會拋出異常? – ndm13

+1

@ ndm13是的,它會是:「java.util.concurrent.ExecutionException:java.lang.Error:sample error」。我已經更新了答案。 – MGorgon

+1

@ ndm13我已經更新了在這裏使用普通Runnable的答案。錯誤可以從它拋出而不拋出聲明。 – MGorgon

0

一個Future是代表運行你的操作,無論是準備的結果,現在或在某一時刻的對象在將來。一般來說,爲了得到結果,人們會調用get方法,特別是如果您將Callable而不是Runnable傳入ExecutorService

如果您的Runnable/Callable引發異常,則這將反映在Future對象中。您將可以通過調用get方法來測試Runnable是否成功運行。如果這是乾淨的運行,你會得到(在這種情況下)null回來。如果拋出異常,則get將拋出ExecutionException,並將該異常標記爲原因。

想象它像一個音樂場地的外套檢查。如果他們失去了你的外套,他們可能不會告訴你,直到你拿着你的外套要求你的外套。 Future爲您的票的目的在這裏。

1

試圖處理從一個線程中拋出的錯誤似乎是一個可疑的使用情況......

從Java文檔:「一個錯誤是Throwable的子類,表示嚴重的問題,合理的應用程序不應該試圖大多數這樣的錯誤都是異常情況,ThreadDeath錯誤雖然是一個「正常」條件,但也是Error的一個子類,因爲大多數應用程序不應該試圖捕獲它。

我需要處理錯誤的唯一時間是當我使用第三方,嚴重管理他們的異常,拋出錯誤,而不是RuntimeException。在這種特定情況下,您需要捕獲錯誤(或Throwable)以避免意外的應用程序崩潰。

+0

因此,從本質上講,最好的做法是引發RuntimeException並像處理其他異常一樣處理它,即使Error通常會拋出,如果它在線程內? – ndm13

+1

Personnally我沒有找到任何理由爲什麼你應該在你的代碼中拋出錯誤。 現在,如果您需要使用您不負責的代碼(例如第三方),則會拋出錯誤,問題是:我應該捕捉它還是不知道。這是一個個案的事情,但我會說,如果它的原因是可恢復的,你可以捕獲錯誤。 例如,假設您的Web應用程序使用第三方,如速度由於無效模板而引發錯誤,我相信您應該捕獲並處理錯誤,因爲Web服務可以繼續運行。 –