[concurrency-interest] CompletableFuture with delay

Millies, Sebastian Sebastian.Millies at softwareag.com
Mon Aug 25 11:11:00 EDT 2014

From: Peter Levart [mailto:peter.levart at gmail.com]
Sent: Monday, August 25, 2014 4:17 PM
To: Millies, Sebastian; concurrency-interest at cs.oswego.edu
Subject: Re: [concurrency-interest] CompletableFuture with delay

On 08/25/2014 09:55 AM, Millies, Sebastian wrote:

Hello there,

I'd like to simulate asynchronous IO events in a test system (without actual IO).

For this purpose, I have defined a method that creates a future which returns a value after a delay.

In contrast to Future#get(Long,TimeUnit) this method does not wait, but uses a separate ScheduledFuture

to complete the future. Code is shown below.

So far, so good. But I also want to cancel the scheduled task (the ScheduledFuture returned from

ScheduledExecutorService#schedule()) when the future is cancelled before the timeout. (So that

I can keep my timeout tasks from piling up when timeouts are long in relation to the real


Please look at the code below, where I create an additional future with whenComplete and use

that to cancel the task. Is that a correct solution? Or could this additional future be optimized

away or be garbage collected? Does the variable "future" that is returned from the method hold a

reference to the additional future? I couldn't tell from the source code of CompletableFuture.

  private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(TIMER_THREADS);

  public static <T> CompletableFuture<T> delayedSuccess(T value, int delay, TimeUnit unit) {

    CompletableFuture<T> future = new CompletableFuture<T>();

    ScheduledFuture<Boolean> task = scheduler.schedule(() -> future.complete(value), delay, unit);

    future.whenComplete((t, ex) -> {

      if (future.isCancelled())

        task.cancel(true);               // <== HERE


    return future;


>Hi Sebastian,
>The returned CompletableFuture 'future' references the BiConsumer function (in
>your case a lambda) until it completes. At that time it invokes it and
>releases the reference to it, so it can be GC-ed. The lambda captures the
>ScheduledFuture 'task' so it implicitly references it, when the lambda goes
>away, so would the captured 'task'.
>But the returned 'future' (and BiConsumer lambda referenced by it) is captured
>by another lambda, the Runnable passed to scheduler.schedule(). This lambda
>and ScheduledFuture 'task' are referenced by the 'scheduler' until the time
>comes and the scheduler executes the delayed task and then they are released.
>This happens even when the ScheduledFuture 'task' is canceled beforehand.
>Unless you configure ScheduledThreadPoolExecutor to remove the task on cancel:
>((ScheduledThreadPoolExecutor) scheduler).setRemoveOnCancelPolicy(true);
>By setting that, I think you get the timely clean-up of all the objects
>Regards, Peter

Hello Peter,

thank you for the detailed explanation.
I suppose there is no way to avoid the downcast?


Software AG – Sitz/Registered office: Uhlandstraße 12, 64297 Darmstadt, Germany – Registergericht/Commercial register: Darmstadt HRB 1562 - Vorstand/Management Board: Karl-Heinz Streibich (Vorsitzender/Chairman), Dr. Wolfram Jost, Arnd Zinnhardt; - Aufsichtsratsvorsitzender/Chairman of the Supervisory Board: Dr. Andreas Bereczky - http://www.softwareag.com

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20140825/a98733b2/attachment-0001.html>

More information about the Concurrency-interest mailing list