0

Using java concurrent executor, future cancel method not stopping the current task.

I have followed this solution of timeout and stop processing of current task. But it doesn't stop the processing. I am trying this with cron job. Every 30 seconds my cron job gets executed and I am putting 10 seconds timeout. Debug comes in future cancel method, but it is not stopping current task. Thank you.

@Scheduled(cron = "*/30 * * * * *")
    public boolean cronTest()
    {
        System.out.println("Inside cron - start ");
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = new Date();
        System.out.println(dateFormat.format(date));
        System.out.println("Inside cron - end ");

        ExecutorService executor = Executors.newCachedThreadPool();
        Callable<Object> task = new Callable<Object>() {
           public Object call() {
              int i=1;
              while(i<100)
              {
                  System.out.println("i: "+ i++);
                  try {
                      TimeUnit.SECONDS.sleep(1);
                  }
                  catch(Exception e)
                  {
                  }
              }
              return null;
           }
        };
        Future<Object> future = executor.submit(task);
        try {
           Object result = future.get(10, TimeUnit.SECONDS); 
        } catch (Exception e) 
        } finally {
           future.cancel(true);
           return true;
        }
    }

The expected result is cron job runs every 30 seconds and after 10 seconds it should time out and wait for approximately 20 seconds for a cron job to start again. And should not continue the older loop because we have timeout on 10 seconds.

Current result is:

Inside cron - start 
2019/07/25 11:09:00
Inside cron - end 
i: 1
i: 2
i: 3
i: 4 ... upto i: 31
Inside cron - start 
2019/07/25 11:09:30
Inside cron - end 
i: 1
i: 32
i: 2
i: 3
i: 33
...

Expected result is:

Inside cron - start 
2019/07/25 11:09:00
Inside cron - end 
i: 1
i: 2
i: 3
i: 4 ... upto i: 10
Inside cron - start 
2019/07/25 11:09:30
Inside cron - end 
i: 1
i: 2
i: 3 ... upto i:10
Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28

1 Answers1

2

The first problem is in this part of code:

catch(Exception e)
{
}

When you invoke future.cancel(true); your thread is being interrupted with Thread.interrupt()
Which means that when a thread is sleeping, it gets awoken and throws InterruptedException which is caught by the catch block and ignored. To fix this problem you have to handle this exception:

catch(InterruptedException e) {
    break; //breaking from the loop
}
catch(Exception e)
{
}

The second problem: Thread.interrupt() may be invoked while the thread is not sleeping. In this case InterruptedException is not thrown. Instead, the interrupted flag of the thread is raised. What you have to do is to check for this flag from time to time, and if it's raised, handle interruption. The basic code for it would look like:

try {
     if (Thread.currentThread().isInterrupted()) {
            break;
     }
     TimeUnit.SECONDS.sleep(1);
}

       ...

// rest of the code

UPDATE:
Here's the full code of Callable:

Callable<Object> task = new Callable<Object>() {
           public Object call() {
              int i=1;
              while(i<100)
              {
                  System.out.println("i: "+ i++);
                  try {
                      if (Thread.currentThread().isInterrupted()) {
                          break; //breaking from the while loop
                      }
                      TimeUnit.SECONDS.sleep(1);
                  } catch(InterruptedException e) {
                      break; //breaking from the while loop
                  } catch(Exception e)
                  {
                  }
              }
              return null;
           }
        };
Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28
  • Thanks for the answer. I tried using ```catch (TimeoutException ex) { Thread.currentThread().interrupt(); future.cancel(true); } catch(InterruptedException e) { Thread.currentThread().interrupt(); future.cancel(true); }catch (Exception e) { Thread.currentThread().interrupt(); future.cancel(true); }``` and [refer 2nd comment]. But still isn't giving the expected result. – Interstellar Jul 25 '19 at 20:21
  • Other change that I made is: `try { if (Thread.currentThread().isInterrupted()) { Thread.currentThread().interrupt(); future.cancel(true); } TimeUnit.SECONDS.sleep(1); } catch (Exception e) { Thread.currentThread().interrupt(); future.cancel(true); }` – Interstellar Jul 25 '19 at 20:22
  • 1
    @Interstellar, use the code I provided in the answer. There's no need to catch `TimeoutException` and invoke `Thread.currentThread().interrupt();` Just break out of the loop once `Thread.currentThread().isInterrupted()` is true. Check the updated answer. – Pavel Smirnov Jul 25 '19 at 20:47
  • It is working with your solution. I appreciate it. Thanks a lot. So the related question is, here we are changing code inside call() to get expected result. If I am calling some external method then can I stop executing it without doing anything in call() method? – Interstellar Jul 25 '19 at 21:02
  • @Interstellar, if you mean interrupt the thread in an external method, then yes, you can. Your goal is to get to the end of the `call()` block, same as in a single-thread app. – Pavel Smirnov Jul 25 '19 at 21:09
  • So how can I achieve the end of `call()` block? The future.cancel(true) is not stopping that executing thread. – Interstellar Jul 25 '19 at 21:14
  • @Interstellar, same way you exit from any method. When there're no more instructions to perform, you exit from it. Same with `call()` . – Pavel Smirnov Jul 25 '19 at 21:18
  • I got your point, but my question is, without modifying `call()` method's while loop / any logic inside `call()`, with what code modification with `Future` object, can I achieve the result as I stated in question? The reason is, there are 3rd party methods and I don't have control over it. So without touching it, I want to stop it's execution after 10 seconds. Thank you. – Interstellar Jul 25 '19 at 21:30
  • 1
    @Interstellar, you can not do it with `Future` or `ExecutorService`. And actually, you do not need to do it. Imagine, what would happen if a thread gets stopped and leaves some resources unreleased?. Or simply owns a lock and never releases it. It's prone to mysterious bugs in your app. Any multithreaded code **must** handle interruption requests properly. If the 3rd party methods do not do that, you should probably consider it as a bug. – Pavel Smirnov Jul 25 '19 at 21:45