Home > Software engineering >  Do we have alternative to Java 9 CompletableFuture methods to handle timeouts?
Do we have alternative to Java 9 CompletableFuture methods to handle timeouts?

Time:12-16

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.

  • Related