【问题标题】:ExecutorService invokeall not getting interruptedExecutorService invokeall 没有被中断
【发布时间】:2018-02-12 19:19:02
【问题描述】:

我有以下代码:

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"。但这不会发生。有人可以帮我理解为什么吗? 如果我在 for 循环之前将 Thread.sleep(2000); 放在 public Boolean call() throws Exception { 的 try 块中,那么我会收到 InterruptedException。这种行为很奇怪。 PS:这只是我为了展示我的问题而编造的一个虚拟示例。

【问题讨论】:

  • 当代码被 io/lock 阻塞,睡眠等时,你会得到java.lang.InterruptedException。在其他情况下,你需要使用Thread.currentThread().isInterrupted()检查当前线程是否被中断
  • 那么在 invokeAll 中设置超时有什么意义呢?

标签: java multithreading executorservice callable


【解决方案1】:

通过向执行任务的线程发送中断来实现取消。但是,与往常一样,通过中断取消是一项合作努力。 被中断的任务应该定期检查它正在运行的线程的中断标志,然后尽早停止运行。认为中断线程会自动导致在其上运行的代码中抛出 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;
}

【讨论】:

    【解决方案2】:

    总之你不能中断没有抛出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 线程。如何设置ExecutorServiceDaemon 线程watch here。 更多有关 Java 中断的详细信息,请访问here

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-01
        • 1970-01-01
        • 2022-10-17
        • 2018-09-17
        • 1970-01-01
        相关资源
        最近更新 更多