You can use ExecutorService.invokeAll()
to schedule all your requests at once. You can specify a timeout to avoid waiting indefinitely. Upon return, all requests will have completed, faulted, or timed out. Iterate over the tasks and fetch the results, and handle timeouts and partial failures however you like. Note that if you add a timeout, you may want to scale it by the number of requests.
You'll need to decide what degree of parallelism you want. My example will create up to as many threads as there are processors available to run them, but the thread count won't exceed the number of requests. That's pretty aggressive; you could probably get away with fewer threads.
List<String> getPermissions(final List<Integer> sites) throws InterruptedException {
if (sites.isEmpty()) {
return Collections.emptyList();
}
final List<String> permissions = new ArrayList<>();
final List<Callable<String>> tasks = new ArrayList<>();
final int threadCount = Math.min(
Runtime.getRuntime().availableProcessors(),
sites.size()
);
final ExecutorService executor = Executors.newFixedThreadPool(threadCount);
try {
for (final Integer site : sites) {
tasks.add(
new Callable<String>() {
@Override
public String call() throws Exception {
return authenticationService.getUserPermissionsForSite(site);
}
}
);
}
for (final Future<String> f : executor.invokeAll(tasks/*, timeout? */)) {
try {
// If you added a timeout, you can check f.isCancelled().
// If timed out, get() will throw a CancellationException.
permissions.add(f.get());
}
catch (final ExecutionException e) {
// This request failed. Handle it however you like.
}
}
}
finally {
executor.shutdown();
}
return permissions;
}
Like most Java operations that perform a wait, invokeAll
may throw an InterruptedException
. It's up to you whether you want to catch it or let it propagate.