中断正在运行 CompletableFuture 的线程

问题描述 投票:0回答:1

假设我有一个

ExecutorService
在守护线程池之上运行(这样我就不需要显式关闭它):

final var executor = Executors.newFixedThreadPool(
        Runtime.getRuntime().availableProcessors(),
        r -> {
            final var t = new Thread(r);
            t.setDaemon(true);
            return t;
        }
);

—并且我想在某个超时后取消长时间运行的

Future
任务:

final var future = executor.submit(() -> {
    try {
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
        return "42";
    } catch (final InterruptedException ie) {
        System.out.println("Task cancelled.");
        throw ie;
    }
});

try {
    System.out.println(future.get(5L, TimeUnit.SECONDS));
} catch (final TimeoutException ignored) {
    System.out.println("Timed out, cancelling...");
    future.cancel(true);
}

运行时,此代码将在大约 5 秒内产生以下输出:

Timed out, cancelling...
Task cancelled.

这意味着当前运行任务的线程确实会被中断,并且

Thread.sleep()
会抛出
InterruptedException
。现在,考虑一下我想改用
CompletableFuture
API。这是语义相同的代码:

final var future = CompletableFuture.supplyAsync(() -> {
    try {
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
        return "42";
    } catch (final InterruptedException ie) {
        System.out.println("Task cancelled.");
        throw new RuntimeException(ie);
    }
}, executor);

或者我是这么想的。然而,代码的第二个版本只会产生以下输出:

Timed out, cancelling...

后台执行器线程将继续运行

Thread.sleep()
,而不会被中断,因此这两个版本的代码在语义上并不相同。

同样,如果我调用

future.orTimeout(timeout, unit)
而不是
future.get(timeout, unit)
,后台线程也不会被中断。后台线程将继续运行(可能)计算量大的任务,直到 JVM 关闭,并且似乎没有办法中断它们。

中断后台线程的唯一方法是调用

executor.shutdownNow()
(仅仅
executor.shutdown()
是不够的),但这当然不是解决方案,因为我想在任务取消后重用执行器。

问题:

  1. 如何取消一个
    CompletableFuture
    中断相应的工作线程,以及
  2. 此行为是否记录在任何地方?

编辑:有一个类似的问题:How to cancel Java 8 completable future?

java multithreading future executorservice completable-future
1个回答
0
投票

未来可完成的 API 并不期望您使用中断来控制程序的流程。

方法 CompletableFuture#cancel 接受

mayInterruptIfRunning
参数,但 api 文档声明

mayInterruptIfRunning - 该值在此实现中无效,因为不使用中断来控制处理。

本质上,任何等待未来的事情都会因异常而停止等待。但任务本身并不知道这一点。

© www.soinside.com 2019 - 2024. All rights reserved.