How to add a timeout to a task
Short answer
Future.get() with a timeout waits no longer than the given time and throws a TimeoutException:
void main() throws Exception {
try (var pool = Executors.newSingleThreadExecutor()) {
Future<String> future = pool.submit(() -> {
Thread.sleep(2000);
return "report";
});
try {
IO.println(future.get(300, TimeUnit.MILLISECONDS));
} catch (TimeoutException e) {
future.cancel(true);
IO.println("Too slow, cancelled: " + future.isCancelled());
}
}
}Output:
Too slow, cancelled: truecancel(true) interrupts the task's thread; otherwise the pool would wait for it to finish when closing.
A default value
completeOnTimeout() supplies a value if the task did not finish in time:
void main() {
String result = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return "fresh data";
})
.completeOnTimeout("cached data", 200, TimeUnit.MILLISECONDS)
.join();
IO.println(result);
}Output:
cached dataAn error on timeout
orTimeout() completes the task with a TimeoutException:
void main() {
var future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return 42;
}).orTimeout(200, TimeUnit.MILLISECONDS);
try {
future.join();
} catch (CompletionException e) {
IO.println(e.getCause().getClass().getSimpleName());
}
}Output:
TimeoutExceptionCommon mistake
Calling get() without a timeout for tasks that use the network. If the server never answers, the program waits forever. Set a limit both on the operation itself (for example, an HTTP client timeout) and on waiting for the result.
Functions used in this recipe
- Future.get() — Waits for a task to finish and returns its result.
- CompletableFuture.supplyAsync() — Runs a computation asynchronously and returns a `CompletableFuture`.