2017-07-03 10 views
5

ExecutorServiceに送信されたタスクをキャンセルして、対応するスレッドがキューから新しいタスクを選択できるようにします。javaで将来のタスクをキャンセルする

この質問はこのフォーラムで何度も答えられています.... Thread.currentThread().interrupt()またはcatch (InterruptedException e)のようにチェックしてください。しかし、コントロールの流れが複数の方法にまたがっている場合は、これらのチェックを行うことでコードが不器用になります。可能であれば、この機能を実現するために、Javaでいくつかの優雅な方法を提案してください。

私が直面している問題はfuture.cancelが実際にタスクをキャンセルしないことです。代わりに実行中のタスクにInterruptedExceptionを送信するだけで、スレッド自身を完了してスレッドを解放するのはタスクの責任です。

だから何私がやったことは、私は例外が明らかに良い見ていない実行中の任意の場所に投げられますたびのコードの下にブロックを入れていたということです!

if(e instanceof InterruptedException) { 
        throw e; 
       } 

だから、どのように次のコードスニペットでこの機能を達成するために:その時点で、それはすでに手遅れだからあなたは、Callableかを中断した場合

public class MonitoringInParallelExp { 

     public static void main(String[] args) throws InterruptedException { 
      MyClass1 myClass1 = new MyClass1(); 
      ExecutorService service = Executors.newFixedThreadPool(1); 
      Future<String> future1 = service.submit(myClass1); 
      Thread.sleep(2000); 
      System.out.println("calling cancel in Main"); 
      future1.cancel(true); 
      System.out.println("finally called cancel in Main"); 
      service.shutdown(); 
     } 
    } 

    class MyClass1 implements Callable<String> { 
     @Override 
     public String call() throws Exception { 
      try{ 
       MyClass2 myClass2 = new MyClass2(); 
       myClass2.method2(); 
      } catch (Exception e){ 
       if(e instanceof InterruptedException) { 
        System.out.println("call:"+"e instanceof InterruptedException="+"true"); 
        throw e; 
       } 
       System.out.println("Got exception in method1. " + e); 
      } 
      System.out.println("returning Myclass1.method1.exit"); 
      return "Myclass1.method1.exit"; 
     } 
    } 

    class MyClass2 { 
     public void method2() throws Exception{ 
      try{ 
       MyClass3 myClass3 = new MyClass3(); 
       myClass3.method3(); 
      } catch (Exception e){ 
       if(e instanceof InterruptedException) { 
        System.out.println("method2:"+"e instanceof InterruptedException="+"true"); 
        throw e; 
       } 
       System.out.println("Got exception in method2. " + e); 

       // in case the exception isn't InterruptedExceptionm, do some work here 
      } 
     } 
    } 

    class MyClass3 { 
     public void method3() throws Exception{ 
      try{ 
       Thread.sleep(10000); 
      } catch (Exception e){ 
       if(e instanceof InterruptedException) { 
        System.out.println("method3:"+"e instanceof InterruptedException="+"true"); 
        throw e; 
       } 
       System.out.println("Got exception in method3. " + e); 
       throw new MyException(); 
      } 
     } 
    } 

    class MyException extends Exception { 

    } 

答えて

4

それは問題ではありませんが

try{ 
    MyClass2 myClass2 = new MyClass2(); 
    myClass2.method2(); 
} catch (Exception e){ 

Thread.sleep(2000)の後にfuture1.cancel(true);に電話すると、実際にはがキャンセルされます。進行中のタスク(この場合はmethod2コール)は、開始前にキャンセルされている必要があります。

ドキュメントはアウト https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(boolean)

試みは、このタスクの実行をキャンセルすることを指します。タスクが既に完了しているか、すでにキャンセルされているか、または何らかの理由でキャンセルできなかった場合、この試行は失敗します。成功した場合、cancelが呼び出されたときにこのタスクが開始されていない場合、このタスクは決して実行されません。タスクが既に開始されている場合、mayInterruptIfRunningパラメータは、このタスクを実行しようとしているスレッドがタスクを停止しようとする際に中断されるべきかどうかを決定します。

進行中のタスクをキャンセルする場合は、volatile booleanフラグなどを使用します。

+0

future1.cancel(true)は、主スレッドが2000 msだけスリープしているのに対して、method3は10000 ms完了するため、InterruptedExceptionを送信します。 method3は、例外がinstanceof InterruptedExceptionかどうかをチェックします。そうであれば、それは呼び出し元に渡されます(method2)。 私は、ワークフロー全体でこのチェックをどこにでも残して、最終的にMyClass1.call()メソッドに渡すことを心配していません。 この機能を実現する他のエレガントな方法はありますか? – gautam

+0

@gautam - コールバックメカニズムを使用します。オブジェクトを他のクラスに渡し、InterruptedExceptionの間にメソッドをキャッチします。コールバックメカニズムについては、https://stackoverflow.com/questions/36449040/executing-java-callback-on-a-new-thread/36451028#36451028を参照してください。 –