2016-10-27 17 views
2

私は多くの投稿をExecutorServiceについて読んでいますが、私が必要とする方法を見つけることができません。Shutdown now on 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

。 – Zymus

+0

私は同意しますが、各スレッドの失敗に関する情報はどこにありますか? 2つ以上のスレッドがある場合は、質問が行われた後にスローされます。多分私のアプローチは間違っていますが、私は良いものを見ません。 – abdiel

+0

ドキュメントから:積極的に実行中のタスクの処理を停止しようとするベストエフォート型の試み以外の保証はありません。例えば、典型的な実装はThread.interrupt()を介して取り消されるので、割り込みに応答しないタスクは決して終了しないかもしれません。 - あなたの仕事が中断されるケースを実際に処理する必要があります。タスク1は、タスク2が中断したときに既に実行されていました。 – pandaadb

答えて

1

あなたの問題は、この方法は、それがそう起こるものは何でも、少なくとも5秒のタスクの期間を待たせ時間のかかる作業に対応する第一Futureインスタンス上で、メインスレッドのコールf.get()を作るexecuteFuturesという事実によるものです。完了すると、既に終了しているFuturef.get()を呼び出して、からすぐにCustomExceptionを取得し、executorService.shutdownNow()を呼び出しますが、割り込みが残っているタスクがなくなると既に遅すぎます。

CustomExceptionがスローされたときにスレッドプールを自動的にシャットダウンするタイプCallableのデコレータを使用すると、スレッドプールはスレッドプールをスローするタスクを実行したスレッドによって直接シャットダウンされますメインスレッドを使用する代わりに例外が発生します。

このような何か:

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(); 
      } 
     } 
    } 
} 

出力:あなたは、現在のスレッドが中断されたかどうかを確認するために、タスク1用のコードのどこにチェックしません

Task 2 done 
Received:Error on task 2 
+0

あなたはうまくいっていますので、私は答えを加えましたが、メッセージ「Received:Error on Task 2」がスローされなかったので、成功した実行のように見えます。 – abdiel

+0

いいえ、 'f.get()'への最初の呼び出しで、 'catch'がループ外で実行され、現在のコードが' InterruptedException'、ループ内を移動して、期待通りの結果を得ます –

+1

ありがとうございます、それは魅力的です。 – abdiel