How to interrupt CompletableFuture::join?
Asked Answered
R

3

9

I've found that CompletableFuture::join seems uninterruptible when not completed:

// CompletableFuture::join implementation from JDK 8 sources
public T join() { 
    Object r;
    return reportJoin((r = result) == null ? waitingGet(false) : r);
}

In above implementation, waitingGet(false) will ignore the interrupt flag of the working Thread and continue waiting. I'm wondering how can I interrupt a Thread in which I call CompletableFuture::join.

Repro answered 28/5, 2017 at 19:48 Comment(2)
I wonder if this is what you are looking for : #43390394Actium
@Actium Thank you for the link. I do know CompletableFuture::cancel won't interrupt the thread. What I want to do is to interrupt a thread which blocks in a CompletableFuture::join operation. Maybe I should describe my question more clearly.Repro
L
11

Do not use join() if you want to support interruption, use get() instead. Basically they are the same except:

  • join() is only defined in CompletableFuture whereas get() comes form interface Future
  • join() wraps exceptions in CompletionException whereas get() wraps them in ExecutionException
  • get() might be interrupted and would then throw an InterruptedException

Note that what you interrupt is the Thread, not the Future. For example, the following code interrupts the main thread while it is waiting on myFuture.get():

CompletableFuture<Void> myFuture = new CompletableFuture<>();
Thread mainThread = Thread.currentThread();
CompletableFuture.runAsync(() -> {
    try {
        Thread.sleep(1000);
        System.out.println("Interrupting…");
        mainThread.interrupt();
        Thread.sleep(1000);
        System.out.println("Completing");
        myFuture.complete(null);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
});
try {
    myFuture.get();
    System.out.println("Get succeeded");
} catch (Exception e) {
    System.out.println("Get failed");
    e.printStackTrace();
}

Output:

Interrupting…
Get failed
java.lang.InterruptedException
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:347)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
    at CompletableFutureInteruption.main(CompletableFutureInteruption.java:37)
    …

If you replace get() by join(), the interrupt will indeed not work.

Laureenlaurel answered 29/5, 2017 at 14:14 Comment(3)
Thank you for pointing out the differences between join() and get().Repro
The method join() is defined in CompletableFuture, not CompletionStage (first bullet point). Though the interface and implementation appear tightly coupled considering the toCompletableFuture() method of CompletionStage.Willson
Thanks @Slaw, I fixed that nowLaureenlaurel
R
2

I finally give up to interrupt the Thread which blocks in waiting for CompletableFuture::join finish.

Instead I use CompletableFuture::allof to get a CompletableFuture all which ends when all my joined Futures end. And then just call the get() method of the all Future in the working thread. When get() returns, I then collect all my results by iterating all my joined Futures and call getNow on them. Such a procedure is interruptible.

Repro answered 29/5, 2017 at 15:12 Comment(5)
Looks like this was an XY problem then…Laureenlaurel
@DidierL Yes, I just want an interruptible way to "join" all the Futures and get the results. I should clarify with that.Repro
I don't see how the allOf() and the interrupts are actually related to one another here. Either allOf() solves the problem without interrupts, or you need interrupts but it does not matter that you are working on the result of an allOf() call.Laureenlaurel
It is get() that is related to interrupts. You are right. Actually I had a bunch of Futures and wanted to join them and get the results. So I mentioned allOf() combined with get().Repro
So how did you actually do an interruption ? example please ?Thirza
V
0

I clearly known in the end you are not actually looking for the interruptible, but as a way around it can be interrupted by exceptions as follows (though it's join()):

private static void testCompleteExceptionally() {
    String name = "Hearen";
    CompletableFuture<String> completableFuture
            = CompletableFuture.supplyAsync(() -> {
        delay(500L);
        if (name == null) {
            throw new RuntimeException("Computation error!");
        }
        return "Hello, " + name;
    });

    if (name != null) {
        completableFuture.completeExceptionally(new RuntimeException("Calculation failed!"));
    }
    out.println(completableFuture.handle((s, t) ->  s != null ? s : "Hello, Stranger!" + t.toString()).join());
}
Violette answered 10/6, 2018 at 2:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.