ExecutorService invokeAll死锁
ExecutorService invokeAll deadlock
在为 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...";
}
}
Task2
和 Task3
代码也相同,除了这两个 类 在 for 循环检查中使用 Integer.MAX_VALUE
。
当我 运行 这个程序时,它永远卡住了,有趣的是,其他两个任务也没有 运行。我的机器有 8 核处理器。想知道为什么会出现这种行为吗?
如果我将 Long.MAX_VALUE 更改为 Integer.MAX_VALUE,一切正常。
另一个有趣的观察是:
如果 submit()
这些任务单独调用,而不是调用 invokeAll()
,除了 Task1(有 Long.MAX_VALUE)之外的其他两个任务按时完成。
您观察到的这种行为确实是预期的行为。考虑以下因素:
1) Task3 计数为 Integer.MAX_VALUE - 5000,而 Task1 和 Task2 计数为 Long.MAX_VALUE - 5000。在所有条件相同的情况下,这意味着 Task1 和 Task2 将持续 2^32比 Task3 长 3 倍,超过 40 亿倍。
2) 在 Javadoc 中为 AbstractExecutorService.invokeAll() 指定(强调我的):
Executes the given tasks, returning a list of Futures holding their
status and results when all complete.
因此,正在发生的事情是 Task3 完成得非常快,但其他两个任务将 运行 持续数天或更可能持续数月。由于 invokeAll()
只有 return 一次 所有任务 完成,您实际上永远不会看到它 return.
您可以通过在 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
。举个例子,即使任务 3 只在 1 毫秒内完成,其他两个任务仍然需要 46 天以上才能完成。
附带说明一下,我不确定您想在 for
循环中使用 val += i
完成什么,因为这会导致许多整数溢出。
在为 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...";
}
}
Task2
和 Task3
代码也相同,除了这两个 类 在 for 循环检查中使用 Integer.MAX_VALUE
。
当我 运行 这个程序时,它永远卡住了,有趣的是,其他两个任务也没有 运行。我的机器有 8 核处理器。想知道为什么会出现这种行为吗?
如果我将 Long.MAX_VALUE 更改为 Integer.MAX_VALUE,一切正常。
另一个有趣的观察是:
如果 submit()
这些任务单独调用,而不是调用 invokeAll()
,除了 Task1(有 Long.MAX_VALUE)之外的其他两个任务按时完成。
您观察到的这种行为确实是预期的行为。考虑以下因素:
1) Task3 计数为 Integer.MAX_VALUE - 5000,而 Task1 和 Task2 计数为 Long.MAX_VALUE - 5000。在所有条件相同的情况下,这意味着 Task1 和 Task2 将持续 2^32比 Task3 长 3 倍,超过 40 亿倍。
2) 在 Javadoc 中为 AbstractExecutorService.invokeAll() 指定(强调我的):
Executes the given tasks, returning a list of Futures holding their status and results when all complete.
因此,正在发生的事情是 Task3 完成得非常快,但其他两个任务将 运行 持续数天或更可能持续数月。由于 invokeAll()
只有 return 一次 所有任务 完成,您实际上永远不会看到它 return.
您可以通过在 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
。举个例子,即使任务 3 只在 1 毫秒内完成,其他两个任务仍然需要 46 天以上才能完成。
附带说明一下,我不确定您想在 for
循环中使用 val += i
完成什么,因为这会导致许多整数溢出。