ExecutorService调用所有死锁

时间:2015-01-13 18:44:59

标签: java-7 executorservice java.util.concurrent

在为ExecutorService invokeAll编写骨架程序时,我遇到了一个有趣的场景,似乎造成了僵局。无法弄清楚为什么会发生这种情况。

这是一个实例化3个任务并调用invokeAll()

的程序
        int poolSize = Runtime.getRuntime().availableProcessors();
        ExecutorService pool = Executors.newFixedThreadPool(poolSize);
        Set<Callable<Object>> tasksSet = new HashSet<>();
        tasksSet.add(new Task1());
        tasksSet.add(new Task2());
        tasksSet.add(new Task3());

        List<Future<Object>> resultSet = pool.invokeAll(tasksSet);
        for (Future<Object> future : resultSet) {
            Object result;
            try {
                   result = future.get(5, TimeUnit.SECONDS);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
                Logger.getLogger(CallableDemo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (ExecutionException ex) {
                ex.printStackTrace();
                Logger.getLogger(CallableDemo.class.getName()).log(Level.SEVERE, null, ex);
            } catch (TimeoutException ex) {
                ex.printStackTrace();
                Logger.getLogger(CallableDemo.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        pool.shutdown();

Task1代码:

public class Task1 implements Callable<Object> {

    @Override
    public Object call() throws Exception {
        long val = 0;
        for (long i = 0; i < Long.MAX_VALUE - 5000; i++) {
            val += i;
        }
        return "Sucessfull Task1 object...";
    }

}
除了这两个类在for循环检查中使用Task2之外,

Task3Integer.MAX_VALUE代码也相同。

当我运行这个程序时,它永远陷入困境,有趣的是其他两个任务也没有运行。我的机器是8核处理器。有什么想法为什么这种行为?

如果我将Long.MAX_VALUE更改为Integer.MAX_VALUE,一切正常。

另一个有趣的观察是:

而不是单独调用invokeAll(),如果submit()这些任务,除了Task1(具有Long.MAX_VALUE)之外,其他两个任务按时完成。

1 个答案:

答案 0 :(得分:2)

您观察到的这种行为确实是预期的行为。请考虑以下事项:

1)Task3计数到Integer.MAX_VALUE - 5000,而Task1和Task2计数到Long.MAX_VALUE - 5000.所有条件相同,这意味着Task1和Task2的持续时间比Task3长2 ^ 32倍,这是超过 40亿次

2)正如AbstractExecutorService.invokeAll()的Javadoc所指定的那样(强调我的):

  

执行给定的任务,返回持有他们的期货清单   状态和结果完成后

所以,发生的事情是Task3很快完成,但其他两个任务将持续数天甚至更长时间。由于invokeAll()仅在所有任务完成后才返回,因此您实际上永远不会看到它返回。

您可以通过在for循环后立即在Task3中打印跟踪来轻松验证这一点,例如:

public static class Task3 implements Callable<Object> {
  @Override
  public Object call() throws Exception {
    long start = System.nanoTime();
    long val = 0;
    for (long i = 0; i < Integer.MAX_VALUE - 5000; i++) {
      val += 1;
    }
    double elapsed = (System.nanoTime() - start) / 1_000_000d;
    System.out.printf("Task3 terminated in %.3f ms%n", elapsed);
    return "Sucessfull Task3 object...";
  }
}

在我的8核机器上,它给了我:Task3 terminated in 568.938 ms。要提出一个想法,即使Task3仅在1毫秒内完成,其他两个任务仍需要46天才能完成。

另一方面,我不确定您希望在val += i循环中使用for完成什么,因为这会导致许多整数溢出。