I want a way to handle timeouts(custom value) for completable futures where we can assign a default value to the future object if there is a timeout. I know there are a couple of methods to handle it in Java 9 and above.
But is there any alternative to Java 9 CompletableFuture methods similar to
public CompletableFuture<T> completeOnTimeout(T value, long timeout, TimeUnit unit)
OR
public CompletableFuture<T> orTimeout(long timeout, TimeUnit unit)
CodePudding user response:
You can use ExecutorService
and then Future#get(long, TimeUnit)
. They are available as of Java 1.5 (the 5th version) if I am not mistaken.
final ExecutorService executorService = Executors.newSingleThreadExecutor();
final Future<String> future = executorService.submit(() -> {
Thread.sleep(1000L);
return "Success";
});
try {
final String result = future.get(1500L, TimeUnit.MILLISECONDS);
System.out.println(result);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
System.out.println("Error");
} finally {
executorService.shutdown();
}
CodePudding user response:
There is no magic in the methods completeOnTimeout
and orTimeout
. When you have something like
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
return "ordinary value";
});
cf.completeOnTimeout("timeout value", 10, TimeUnit.SECONDS);
cf.thenAccept(System.out::println).join();
you can do the same in Java 8 with
static final ScheduledExecutorService TIMEOUT_SCHEDULER
= Executors.newScheduledThreadPool(0);
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.MINUTES.toNanos(1));
return "ordinary value";
});
TIMEOUT_SCHEDULER.schedule(()->cf.complete("timeout value"), 10, TimeUnit.SECONDS);
cf.thenAccept(System.out::println).join();
To be closer to what Java 9’s CompletableFuture
does internally, e.g. use daemon threads, keep at least one thread alive, and having cleanup for cases where the future is completed ordinarily earlier than the timeout, you may use a helper class like
public final class TimeoutSupport {
private static final ScheduledExecutorService TIMEOUT_SCHEDULER;
static {
ScheduledThreadPoolExecutor e = new ScheduledThreadPoolExecutor(1, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
e.setRemoveOnCancelPolicy(true);
TIMEOUT_SCHEDULER = e;
}
public static <T> CompletableFuture<T> orTimeout(
CompletableFuture<T> cf, long timeout, TimeUnit unit) {
return withTimeout(cf,
()->cf.completeExceptionally(new TimeoutException()), timeout, unit);
}
public static <T> CompletableFuture<T> completeOnTimeout(
CompletableFuture<T> cf, T value, long timeout, TimeUnit unit) {
return withTimeout(cf, () -> cf.complete(value), timeout, unit);
}
private static <T> CompletableFuture<T> withTimeout(
CompletableFuture<T> cf, Runnable action, long timeout, TimeUnit unit) {
Future<?> f = TIMEOUT_SCHEDULER.schedule(action, timeout, unit);
cf.whenComplete((o, t) -> f.cancel(false));
return cf;
}
private TimeoutSupport() {}
}
to be used like
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(20));
return "ordinary value";
});
TimeoutSupport.completeOnTimeout(cf, "timeout value", 10, TimeUnit.SECONDS);
cf.thenAccept(System.out::println).join();
or in a fluent style
TimeoutSupport.completeOnTimeout(
CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(20));
return "ordinary value";
}), "timeout value", 10, TimeUnit.SECONDS)
.thenAccept(System.out::println).join();
though not as neat as just chaining, like with Java 9’s methods.
The orTimeout
method works similar.