0

I have following code :

public class Application1 {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        List<Callable<Boolean>> callableTasks = new ArrayList<>();
        callableTasks.add(new LogDownloadCallable());
        callableTasks.add(new LogDownloadCallable());
        callableTasks.add(new LogDownloadCallable());
        callableTasks.add(new LogDownloadCallable());
        callableTasks.add(new LogDownloadCallable());

        List<Future<Boolean>> futures =null;
        try {
          futures = executorService.invokeAll(callableTasks, 1090, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
          System.out.println("invokeall inturrupted !!");
        }
        executorService.shutdownNow();
    }
}


class LogDownloadCallable implements Callable<Boolean> {
    public Boolean call() throws Exception {
    try{
      //This for sure takes days to complete, so should through Cancellation exception because    timeout on invokeall set to 1 minute
      long val = 0;
      for (long i = 0; i < Long.MAX_VALUE - 5000; i++) {
        val += i;
      }
      System.out.println("complete");
    }catch(Exception e){
      System.out.println("Exception ! " +e.toString()+ Thread.currentThread().getId());
      return false;
    }
    return true;
  }
}

I was hoping to get "java.lang.InterruptedException" after 1090 msec timeout. But that doesn't happen. Can somebody help me understand why ? If I put a Thread.sleep(2000); in the try block in public Boolean call() throws Exception {, before the for loop, then I am getting InterruptedException. This behavior is weird. PS : this is just a dummy example I made up to showcase my problem.

voidMainReturn
  • 3,339
  • 6
  • 38
  • 66
  • You get `java.lang.InterruptedException` when the code is blocked on io/lock, sleeping etc. In other cases you need to check if the current thread is interrupted using `Thread.currentThread().isInterrupted()` – Venkata Raju Feb 12 '18 at 19:42
  • Then what's the point of having a timeout in invokeAll ? – voidMainReturn Feb 12 '18 at 19:50

3 Answers3

0

Cancellation happens by sending an interrupt to the thread executing the task. However, as always, cancellation through interruption is a cooperative endeavour. The task being interrupted, should periodically check the interrupted flag of the thread it's running on to then stop running at its earliest convenience. It's a common misconception to think that interrupting a thread will automatically cause an InterruptedException to be thrown in the code running on it.

If the task calls blocking methods (you can recognize them, because they are declared to throw InterruptedException) it will be forced to deal with the thread being interrupted by handling the exception. Thread.sleep() is such a method, which is why you see the timeout work when you add it to your code.

If the task doesn't call any blocking methods, such as when you don't call Thread.sleep() in your code, the only way for the task to know it is interrupted is by checking the thread's interrupt flag itself. For code running in an endless (or long) loop, this is typically done, once per iteration.

Note that some methods block the code running on the thread but are also not responsive to interruption (e.g. blocking IO). They will not throw InterruptedException, and will happily keep doing what they are doing. Making such tasks more responsive to cancellation is a bit trickier, and can be different case by case.

Stopping at the earliest convenience can mean a variety of things. For some tasks, it can mean stopping immediately. For other code it can mean clearing the interrupt flag, running to completion, then setting the interrupt flag again and returning its result.

For your example code, it's obviously going to be the former option.

public Boolean call() throws Exception {
    long val = 0;
    for (long i = 0; i < Long.MAX_VALUE - 5000; i++) {
        if (Thread.currentThread().isInterrupted()) { // explicit check for cancellation
          System.out.println("Exception ! " +e.toString()+ Thread.currentThread().getId());
          return false;
        }
        val += i;
    }
    System.out.println("complete");
    return true;
}
bowmore
  • 10,842
  • 1
  • 35
  • 43
-1

In a few words you can not interrupt the task which did not throw java.lang.InterruptedException. If you'd have for example this code

class LogDownloadCallable implements Callable<Boolean> {
public Boolean call() throws Exception {
    try {
        long val = 0;
        for (long i = 0; i < Long.MAX_VALUE - 5000; i++) {
            val += i;
            Thread.sleep(1); // throws java.lang.InterruptedException
        }
        System.out.println("complete");
    } catch (Exception e) {
        System.out.println("Exception ! " + e.toString() + Thread.currentThread().getId());
        return false;
    }
    return true;
}

}

your app will be work as you wish. In your case I recommend you use Daemon threads those can be stopped immediately. How to set up ExecutorService with Daemon threads watch here. And more detail information about interruption in Java available here.

Lev Khruschev
  • 1,576
  • 2
  • 14
  • 18
-1

For this situation what worked for me was creating the threads with Guava's ThreadFactoryBuilder class.

ExecutorService executorService = Executors.newFixedThreadPool(5, new ThreadFactoryBuilder().setDaemon(true).build());

The threads that get created are deamons and they get killed after the timeout. In the case of interrupted threads I get CancellationException when I do future.get().

voidMainReturn
  • 3,339
  • 6
  • 38
  • 66