ExecutorService调用不会被中断

时间:2018-02-12 19:19:02

标签: java multithreading executorservice callable

我有以下代码:

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

我希望在1090毫秒超时后获得"java.lang.InterruptedException"。但这不会发生。有人可以帮我理解为什么吗? 如果我在Thread.sleep(2000);中的try块中放置public Boolean call() throws Exception {,在for循环之前,那么我将收到InterruptedException。这种行为很奇怪。 PS:这只是我用来展示我的问题的一个虚拟例子。

3 个答案:

答案 0 :(得分:0)

通过向执行任务的线程发送中断来实现取消。然而,一如既往,通过中断取消是一项合作努力。 任务被中断,应该定期检查其运行的线程的中断标志,然后在最早的方便时停止运行。认为中断线程会自动导致InterruptedException被抛出在其上运行的代码中,这是一种常见的误解。

如果任务调用阻塞方法(您可以识别它们,因为它们被声明为抛出InterruptedException),它将被强制处理被处理异常而被中断的线程。 Thread.sleep()就是这样一种方法,这就是为什么在将代码添加到代码中时看到超时工作的原因。

如果任务没有调用任何阻止方法,例如当您不在代码中调用Thread.sleep()时,该任务的唯一方法就是知道它被中断是通过检查线程的中断标志本身。对于在无限(或长)循环中运行的代码,通常每次迭代完成一次。

请注意,某些方法会阻止在线程上运行的代码,但也无法响应中断(例如,阻止IO)。他们不会抛出InterruptedException,并乐意继续做他们正在做的事情。使这些任务对取消更具响应性有点棘手,并且可能因具体情况而异。

尽早停止可能意味着各种各样的事情。对于某些任务,它可能意味着立即停止。对于其他代码,它可能意味着清除中断标志,运行完成,然后再次设置中断标志并返回其结果。

对于您的示例代码,它显然将成为前一个选项。

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

答案 1 :(得分:-1)

简而言之,你不能打断没有抛出java.lang.InterruptedException的任务。如果你有这个代码

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

}

您的应用将按您的意愿工作。 在您的情况下,我建议您使用可以立即停止的Daemon个线程。如何使用ExecutorService个帖子watch here设置Daemon。 有关Java中断的更多详细信息here

答案 2 :(得分:-1)

对于这种情况,对我有用的是使用Guava的ThreadFactoryBuilder类创建线程。

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

创建的线程是deamons,它们会在超时后被杀死。在线程中断的情况下,当我CancellationException时,我会得到future.get()