2016-10-27 253 views
2

我讀了很多關於ExecutorService的帖子,但我無法找到我需要的方式。立即關閉ExecutionException

我需要一些併發線程。當他們中的任何一個拋出自定義異常時,所有剩餘的任務都被取消。

這是我做的一個例子。該任務正在併發工作,但不會在例外情況下中斷。

public class Main { 

    public static void main(String[] args) { 
     ExecutorService executorService = Executors.newFixedThreadPool(2); 
     List<Future> futures = new ArrayList<Future>(); 

     futures.add(executorService.submit(new Callable<Void>() { 
      public Void call() throws Exception { 
       Thread.sleep(5000); 
       System.out.println("Task 1 done"); 
       return null; 
      } 
     })); 

     futures.add(executorService.submit(new Callable<Void>() { 
      public Void call() throws Exception { 
       Thread.sleep(2000); 
       System.out.println("Task 2 done"); 
       if (true) { 
        throw new CustomException("Error on task 2"); 
       } 
       return null; 
      } 
     })); 
     executorService.shutdown(); 

     try { 
      executeFutures(futures); 
     } catch (CustomException ex) { 
      System.out.println("Received:" + ex.getMessage()); 
      executorService.shutdownNow(); 
     }  
    } 

    private static void executeFutures(List<Future> futures) throws CustomException { 
     try { 
      for (Future f : futures) { 
       f.get(); 
      } 
     } catch (ExecutionException | InterruptedException e) { 
      if (e.getCause() instanceof CustomException) { 
       throw (CustomException) e.getCause(); 
      } 
     } 
    }  
} 

這是輸出:

Task 2 done //exception is thrown here but task1 continue. 
Task 1 done 
Received:Error on task 2 

任何幫助將不勝感激。

+0

你不會在代碼的任何地方檢查任務1看看如果當前線程被中斷。 – Zymus

+0

我同意,但哪裏會有關於每個線程失敗的信息? ,如果有兩個以上的線程在問題發生後拋出。也許我的方法是錯誤的,但我看不到一個好的方法。 – abdiel

+0

來自文檔:除了竭盡全力嘗試停止處理主動執行的任務之外,沒有任何保證。例如,典型的實現將通過Thread.interrupt()取消,所以任何不能響應中斷的任務都不會終止。 - 你需要實際處理任務被中斷的情況。當任務2中斷時,任務1已經運行。 – pandaadb

回答

1

你的問題是由於該方法executeFutures使主線程調用f.get()對應於長期的任務,這使得它等待任務的持續時間如此至少5秒鐘,無論發生什麼情況第一Future實例。一旦完成,它將在第二個Future上調用f.get(),這已經結束,因此它立即從ExecutionException得到CustomException,並調用executorService.shutdownNow(),但由於沒有更多任務需要中斷,所以已經太晚了。

你能做什麼,是使用Callable類型的裝飾器,當CustomException被拋出時會自動關閉線程池,這樣線程池就會直接被執行引發任務的線程關閉異常而不是使用主線程。

事情是這樣的:

public class AutoShutdown<V> implements Callable<V> { 

    private final ExecutorService executorService; 
    private final Callable<V> task; 

    public AutoShutdown(final ExecutorService executorService, final Callable<V> task) { 
     this.executorService = executorService; 
     this.task = task; 
    } 

    @Override 
    public V call() throws Exception { 
     try { 
      return task.call(); 
     } catch (CustomException e) { 
      executorService.shutdownNow(); 
      throw e; 
     } 
    } 
} 

然後,你將需要通過爲旁邊的裝飾提交您的任務:

futures.add(
    executorService.submit(
     new AutoShutdown<>(
      executorService, 
      new Callable<Void>() { 
       public Void call() throws Exception { 
        Thread.sleep(5000); 
        System.out.println("Task 1 done"); 
        return null; 
       } 
      } 
     ) 
    ) 
); 

futures.add(
    executorService.submit(
     new AutoShutdown<>(
      executorService, 
      new Callable<Void>() { 
       public Void call() throws Exception { 
        Thread.sleep(2000); 
        System.out.println("Task 2 done"); 
        if (true) { 
         throw new CustomException("Error on task 2"); 
        } 
        return null; 
       } 
      } 
     ) 
    ) 
); 

輸出:

Task 2 done 

,你可以在輸出中看到,任務已經很快中斷了。


消息「Received:Error on task 2」沒有拋出,所以它看起來 像一個成功執行,而不是

不,它只是因爲f.get()第一調用拋出的情況下InterruptedException如預期的那樣使其從executeFutures退出,因爲捕獲是在循環外部執行的,因此將其在循環內部移動如下:

private static void executeFutures(List<Future> futures) throws CustomException { 
    for (Future f : futures) { 
     try { 
      f.get(); 
     } catch (ExecutionException | InterruptedException e) { 
      if (e.getCause() instanceof CustomException) { 
       throw (CustomException) e.getCause(); 
      } 
     } 
    } 
} 

輸出:

Task 2 done 
Received:Error on task 2 
+0

您處於良好的狀態,所以我加上了答案,但沒有拋出「Received:Error on Task 2」的消息,因此它看起來像是成功執行,並非如此。 – abdiel

+0

不,這只是因爲第一次調用'f.get()'拋出一個'InterruptedException',因爲'catch'在循環之外執行,並且當前代碼對' InterruptedException',將其移入循環內部,那麼您將得到你所期望的結果 –

+1

謝謝,它像一個魅力。 – abdiel