I call a method that returns a Future
, once for each element in a List<Principal>
, so I end up with a List<Future<UserRecord>>
.
The method returning Future
is library code and I have no control over how that code gets run, all I have is the Future
.
I want to wait for all the Future
s to finish (success or failure) before proceeding further.
Is there a better way to do so than this:
List<Principal> users = new ArrayList<>();
// Fill users
List<Future<UserRecord>> futures = getAllTheFutures(users);
List<UserRecord> results = new ArrayList<>(futures.size());
boolean[] taskCompleted = new boolean[futures.size()];
for (int j = 0; j < taskCompleted.length; j++) {
taskCompleted[j] = false;
}
do {
for (int i = 0; i < futures.size(); i++) {
if (!taskCompleted[i]) {
try {
results.add(i, futures.get(i).get(20, TimeUnit.MILLISECONDS));
taskCompleted[i] = true;
} catch (TimeoutException e) {
// Do nothing
} catch (InterruptedException | ExecutionException e) {
// Handle appropriately, then...
taskCompleted[i] = true;
}
}
}
} while (allNotCompleted(taskCompleted));
For the curious:
private boolean allNotCompleted(boolean[] completed) {
for (boolean b : completed) {
if (!b)
return true;
}
return false;
}
Unlike in this answer to Waiting on a list of Future I don't have control over the code that creates the Future
s.
Your code can be simplified a lot. An equivalent version could be written as follows, unless you have requirements that you didn't specify in the question.
List<Principal> users = // fill users
List<Future<UserRecord>> futures = getAllTheFutures(users);
List<UserRecord> results = new ArrayList<>();
for (int i = 0; i < futures.size(); i++) {
try {
results.add(futures.get(i).get());
} catch (InterruptedException | ExecutionException e) {
// Handle appropriately, results.add(null) or just leave it out
}
}
}
You could simply do a reductive list; removing successful responses from your list and iterating until empty.
List<Principal> users = // fill users
List<Future<UserRecord>> futures = getAllTheFutures(users);
List<UserRecord> results = new ArrayList<>();
for (int i = 0; i < futures.size(); i++) {
try {
results.add(futures.get(i).get(<how long you want before your application throws exception>));
}
catch (InterruptedException | ExecutionException e) {
// Handle appropriately, results.add(null) or just leave it out
}
catch (TimeoutException timeoutEx) {
// If the Future retrieval timed out you can handle here
}
}
}
Since your intent is to collect a "set" of Jobs before progressing, waiting until you get a return on thread index X in this case will give a time cost of (roughly) the last returned thread.
Or, if you plan to abort all threads in the set if any fail, you can use Java 8 CompletableFuture
CompletableFuture[] cfs = futures.toArray(new CompletableFuture[futures.size()]);
return CompletableFuture.allOf(cfs)
.thenApply(() -> futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList())
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With