如何打断CompletableFuture::join?

How to interrupt CompletableFuture::join?

我发现 CompletableFuture::join 在未完成时似乎 不会中断 :

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

在上面的实现中,waitingGet(false)会忽略正在工作的Thread的中断标志并继续等待。我想知道如何中断我调用 CompletableFuture::join.

Thread

如果要支持中断,请不要使用join(),请改用get()。基本上它们是相同的,除了:

  • join() 仅在 CompletableFuture 中定义,而 get() 来自接口 Future
  • join() 将异常包装在 CompletionException 中,而 get() 将它们包装在 ExecutionException
  • get() 可能会被打断然后抛出 InterruptedException

请注意,您打断的是 Thread,而不是 Future。例如,以下代码在等待 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();
}

输出:

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)
    …

如果把get()换成join(),中断确实不行

我终于放弃中断阻塞等待CompletableFuture::join完成的Thread。

相反,我使用 CompletableFuture::allof 来获得 CompletableFuture all,当我加入的所有 Futures 结束时结束。然后在工作线程中调用 all Future 的 get() 方法。当 get() returns 时,我会通过迭代所有加入的 Futures 并在它们上调用 getNow 来收集所有结果。这样的过程是可中断的。

我清楚地知道你最终不是实际上在寻找可中断,但作为一种绕过它的方法可以被中断异常如下(虽然是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());
}