Reputation: 66637
While writing a skeleton program for ExecutorService invokeAll
I came across an interesting scenario which seems created a deadlock. Couldn't figure out exactly why this is happening.
Here is the program which instantiates 3 tasks and calls 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();
And Task1
code:
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
and Task3
code also same except these two classes use Integer.MAX_VALUE
in for loop check.
When I run this program it stuck forever and interestingly other two tasks also didn't run. My machine has is 8-core processor. Any thoughts why this behavior?
If I change Long.MAX_VALUE to Integer.MAX_VALUE, everything works fine.
Another interesting observation is:
Instead of calling invokeAll()
, if submit()
these tasks individually, two other tasks completed on time except Task1 (which has Long.MAX_VALUE).
Upvotes: 0
Views: 1109
Reputation: 4347
This behavior you observe is indeed the expected one. Consider the following:
1) Task3 counts up to Integer.MAX_VALUE - 5000, whereas Task1 and Task2 count up to Long.MAX_VALUE - 5000. All things being equal, this means that Task1 and Task2 will last 2^32 times longer than Task3, that's over 4 billion times longer.
2) As specified in the Javadoc for AbstractExecutorService.invokeAll() (emphasis mine):
Executes the given tasks, returning a list of Futures holding their status and results when all complete.
So, what is happening is that Task3 completes very quickly, but the other two tasks will run for days or more likely for months. Since invokeAll()
only returns once all tasks have completed, you effectively never see it return.
You can easily verify this by printing a trace in Task3 right after the for
loop, for instance:
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...";
}
}
On my 8-core machine, it gives me: Task3 terminated in 568.938 ms
. To give an idea, even if Task3 were to complete in only 1 ms, it would still take over 46 days for the other two tasks to complete.
On a side note, I'm not sure what you wanted to accomplish with val += i
in your for
loop, since this will result in many integer overflows.
Upvotes: 2