在为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
之外, Task3
和Integer.MAX_VALUE
代码也相同。
当我运行这个程序时,它永远陷入困境,有趣的是其他两个任务也没有运行。我的机器是8核处理器。有什么想法为什么这种行为?
如果我将Long.MAX_VALUE更改为Integer.MAX_VALUE,一切正常。
另一个有趣的观察是:
而不是单独调用invokeAll()
,如果submit()
这些任务,除了Task1(具有Long.MAX_VALUE)之外,其他两个任务按时完成。
答案 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
完成什么,因为这会导致许多整数溢出。