5

I want to cancel a task submitted to ExecutorService thus allowing the corresponding thread to pick a new task from the queue.

Now this question have been answered many times on this forum.... like checking Thread.currentThread().interrupt() or catch (InterruptedException e). But if the flow of control spans across multiple methods then putting these checks makes the code clumsy. So if possible please suggest some elegant ways in java to achieve this functionality.

The problem that I'm facing is that future.cancel won't actually cancel the task. Instead it just sends an InterruptedException to the executing task and it's the task's responsibility to mark itself complete and release the thread.

So what I did is that I had to put the below block of code whenever an exception gets thrown anywhere in the execution which obviously doesn't look good !

if(e instanceof InterruptedException) {
                    throw e;
                }

So, how to achieve this functionality in following code snippet:

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 {

    }
MWiesner
  • 8,868
  • 11
  • 36
  • 70
gautam
  • 197
  • 1
  • 4
  • 17

1 Answers1

5

It does not matter if you interrupt the Callable or not, because at that point it's already too late

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

Your call to future1.cancel(true); after Thread.sleep(2000) does not actually cancel the ongoing task (in this case your method2 call) it only means that it should have been cancelled before it started.

The docs point that out https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#cancel(boolean)

Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

If you want to cancel a ongoing task you want to use a volatile boolean flag or something similiar.

Murat Karagöz
  • 35,401
  • 16
  • 78
  • 107
  • future1.cancel(true) will definitely send the InterruptedException since the main thread slept for 2000 ms only whereas method3 takes 10000 ms to complete. Now method3 will check if the exception is instanceof InterruptedException or not. If it is, then it will pass it to the caller (method2). I don't wan't to keep this check everywhere in the whole workflow to finally pass it to MyClass1.call() method. Is there any other elegant way in this world to achieve this functionality ?? – gautam Jul 03 '17 at 11:52
  • @gautam - Use callback mechanism. Pass the object to other class and catch a method during InterruptedException. Refer to https://stackoverflow.com/questions/36449040/executing-java-callback-on-a-new-thread/36451028#36451028 for Callback mechanism. – Ravindra babu Jul 04 '17 at 12:27