Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to schedule a CompletableFuture?

Is there any way to schedule CompletableFuture in Java? What I wanted to do is to schedule a task to be executed with some delay, and chain it with other operations to be performed asynchronously when it completes. So far I didn't find any way to do this.

For good ol' Futures we have e.g. ScheduledExecutorService, where we can schedule a task to be executed with some delay like this:

ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
Future<String> future = scheduledExecutorService.schedule(() -> "someValue", 10, TimeUnit.SECONDS);

Is there any similar way for CompletableFutures?

like image 904
Łukasz Gruba Avatar asked Nov 05 '19 08:11

Łukasz Gruba


2 Answers

As said, there is support in Java 9.

But it’s not hard to create a similar feature under Java 8; you already named the necessary elements:

// prefer this constructor with zero core threads for a shared pool,
// to avoid blocking JVM exit
static final ScheduledExecutorService SCHEDULER = new ScheduledThreadPoolExecutor(0);
static Executor delayedExecutor(long delay, TimeUnit unit)
{
  return delayedExecutor(delay, unit, ForkJoinPool.commonPool());
}
static Executor delayedExecutor(long delay, TimeUnit unit, Executor executor)
{
  return r -> SCHEDULER.schedule(() -> executor.execute(r), delay, unit);
}

which can be used similarly to the Java 9 feature:

Executor afterTenSecs = delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture<String> future 
  = CompletableFuture.supplyAsync(() -> "someValue", afterTenSecs);

future.thenAccept(System.out::println).join();

Care must be taken to avoid that the shared scheduled executor’s threads prevent the JVM from terminating. The alternative to a zero core pool size is to use daemon threads:

static final ScheduledExecutorService SCHEDULER
  = Executors.newSingleThreadScheduledExecutor(r -> {
    Thread t = new Thread(r);
    t.setDaemon(true);
    return t;
  });
like image 52
Holger Avatar answered Sep 20 '22 09:09

Holger


If you're using Java 9+ then CompletableFuture#delayedExecutor(long,TimeUnit) may fit your needs:

Returns a new Executor that submits a task to the default executor after the given delay (or no delay if non-positive). Each delay commences upon invocation of the returned executor's execute method.

Executor delayed = CompletableFuture.delayedExecutor(10L, TimeUnit.SECONDS);
CompletableFuture.supplyAsync(() -> "someValue", delayed)
    .thenAccept(System.out::println)
    .join();

There's also an overload where you can specify the Executor to use in place of the "default executor".

like image 44
Slaw Avatar answered Sep 18 '22 09:09

Slaw