java 难道CompletableFuture.get(timeout)不应该终止任务吗?

vecaoik1  于 2022-12-02  发布在  Java
关注(0)|答案(2)|浏览(369)

我正在运行CompletableFuture的两个示例,它们等待1秒并向控制台打印一些内容。第一个示例在0.5秒后被中断。所以我希望只有第二个示例打印,但实际上两个示例都打印了。这是怎么回事?
代码如下:

CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> {
    System.out.println("Start CF 1");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println(1);
});

CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> {
    System.out.println("Start CF 2");
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    System.out.println(2);
});

long start = System.currentTimeMillis();
try {
    c1.get(500, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
    System.out.println("CF interrupted after " + (System.currentTimeMillis() - start) + "ms");
}
c2.get();

它会打印:

Start CF 1
Start CF 2
CF interrupted after 510ms
2
1
jjhzyzn0

jjhzyzn01#

你为什么认为应该这样做呢?如果两个消费者都想处理同一个未来的结果,而其中一个准备比另一个等待更长的时间呢?第一个会杀死它,因为第二个可能已经及时完成了。
在任何情况下,Java都没有杀死任务的概念。你能做的最好的事情是设置一个标志,要求它停止--一个中断--但是这依赖于任务检查和遵守这个标志。你的任务是这样做的,因为Thread.sleep()隐式地检查中断标志,但是这是一个人为的例子,很多任务从来不会检查它。

gorkyyrv

gorkyyrv2#

get方法的逾时只会告知何时应该停止执行get方法。它不会影响结果的计算。这适用于所有future。
因此,要中断一个任务,需要调用cancel(true),但在CompletableFuture的情况下,即使这样也不会中断任务:

参数:
  • mayInterruptIfRunning-此值在此实现中不起作用,因为中断不用于控制处理。

如果你想要可中断的任务,你需要使用ExecutorService,但是你也可以使用与CompletableFuture的默认执行器相同的线程池:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    ExecutorService es = ForkJoinPool.commonPool();
    Future<Void> c1 = es.submit(() -> {
        System.out.println("Start CF 1");
        Thread.sleep(1000);
        System.out.println(1);
        return null;
    });

    Future<Void> c2 = es.submit(() -> {
        System.out.println("Start CF 2");
        Thread.sleep(1000);
        System.out.println(2);
        return null;
    });

    long start = System.currentTimeMillis();
    try {
        c1.get(500, TimeUnit.MILLISECONDS);
    } catch(TimeoutException e) {
        c1.cancel(true);
        System.out.println("timeout after "
                + (System.currentTimeMillis() - start) + "ms, canceled");
    }
    c2.get();
}

请注意,中断可能仍然太慢,无法阻止打印1。在我的机器上,我至少需要Thread.sleep(1100);才能在打印之前中断任务。

相关问题