首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何取消Java 8可完成的未来?

如何取消Java 8可完成的未来?
EN

Stack Overflow用户
提问于 2014-04-27 07:03:29
回答 6查看 27.4K关注 0票数 33

我正在玩Java 8可完成的未来。我有以下代码:

代码语言:javascript
运行
复制
CountDownLatch waitLatch = new CountDownLatch(1);

CompletableFuture<?> future = CompletableFuture.runAsync(() -> {
    try {
        System.out.println("Wait");
        waitLatch.await(); //cancel should interrupt
        System.out.println("Done");
    } catch (InterruptedException e) {
        System.out.println("Interrupted");
        throw new RuntimeException(e);
    }
});

sleep(10); //give it some time to start (ugly, but works)
future.cancel(true);
System.out.println("Cancel called");

assertTrue(future.isCancelled());

assertTrue(future.isDone());
sleep(100); //give it some time to finish

使用runAsync,我调度等待锁存的代码的执行。接下来,我取消了未来,希望在里面抛出一个被中断的异常。但是,似乎线程在等待调用时仍然被阻塞,即使未来被取消(断言传递),InterruptedException也不会被抛出。使用ExecutorService的等效代码按预期工作。它是CompletableFuture中的一个bug还是我的示例中的一个bug?

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2014-04-27 21:38:31

很明显这是故意的。CompletableFuture::cancel方法的Javadoc声明:

参数: mayInterruptIfRunning -此值在此实现中没有效应,因为中断不用于控制处理。

有趣的是,ForkJoinTask::cancel方法对参数mayInterruptIfRunning使用了几乎相同的措辞。

我在这个问题上有一个猜想:

  • 中断用于阻塞操作,如睡眠、等待或I/O操作,
  • 但是,CompletableFuture和ForkJoinTask都不打算与阻塞操作一起使用。

而不是阻塞,CompletableFuture应该创建一个新的CompletionStage,而cpu绑定的任务是叉连接模型的先决条件。因此,对他们中的任何一个使用中断都会使他们的目的落空。另一方面,它可能会增加复杂性,如果按预期使用,这不是必需的。

票数 20
EN

Stack Overflow用户

发布于 2017-06-06 18:28:54

当您调用CompletableFuture#cancel时,您只会停止链的下游部分。上游部分,即最终将称为complete(...)completeExceptionally(...)的部分,不会得到任何信号表明不再需要结果。

那些‘上游’和‘下游’的东西是什么?

让我们考虑以下代码:

代码语言:javascript
运行
复制
CompletableFuture
        .supplyAsync(() -> "hello")               //1
        .thenApply(s -> s + " world!")            //2
        .thenAccept(s -> System.out.println(s));  //3

在这里,数据从上到下--从供应商创建、通过函数修改到由println使用。上述特定步骤的部分称为上游,下面的部分称为下游。例如,步骤1和步骤2是步骤3的上游。

下面是幕后发生的事情。这不是精确的,相反,它是一个方便的心智模型,正在发生的事情。

  1. 供应商(步骤1)正在执行(在JVM的公共ForkJoinPool中)。
  2. 然后,complete(...)将供应商的结果传递给下一个CompletableFuture下游。
  3. 在接收到结果后,CompletableFuture调用下一步--一个函数(步骤2),它接受前一步的结果,并返回将进一步传递给下游CompletableFuturecomplete(...)的内容。
  4. 在接收到步骤2的结果后,步骤3 CompletableFuture调用使用者System.out.println(s)。消费者完成后,下游CompletableFuture将收到它的值,(Void) null

正如我们所看到的,这个链中的每个CompletableFuture都必须知道下游谁在等待将值传递给他们的complete(...) (或completeExceptionally(...))。但是CompletableFuture不需要知道任何关于它的上游(或上游-可能有几个)。

因此,在步骤3中调用cancel() 的不会中止步骤1和2,因为从步骤3到步骤2之间没有链接。

假设如果您使用的是CompletableFuture,那么您的步骤就足够小,所以如果执行一些额外的步骤,就不会有什么害处。

如果希望向上游传播取消,则有两个选项:

  • 自己实现--创建一个专用的CompletableFuture (将其命名为cancelled),它将在每一步后进行检查(类似于step.applyToEither(cancelled, Function.identity()))。
  • 使用反应性堆栈,如RxJava 2、ProjectReactor/Flux或Akka流
票数 30
EN

Stack Overflow用户

发布于 2020-12-01 19:09:46

如果您实际上希望能够取消一个任务,那么您必须使用Future本身(例如,由ExecutorService.submit(Callable<T>)返回,而不是CompletableFuture返回)。正如nosid在回答中指出的那样,CompletableFuture完全忽略了对cancel(true)的任何调用。

我怀疑JDK团队没有实现中断,因为:

  1. 中断总是很烦人,人们很难理解,也很难工作。Java /O系统甚至是不可中断的,尽管对InputStream.read()的调用正在阻塞调用!( JDK团队也没有计划让标准的I/O系统再次可中断,就像Java早期那样。)
  2. JDK团队一直在非常努力地从早期的Java时代(如Object.finalize()Object.wait()Thread.stop()等)逐步淘汰旧的坏API。我相信Thread.interrupt()被认为属于最终必须被废弃和替换的一类。因此,较新的API(如ForkJoinPoolCompletableFuture)已经不支持它了。
  3. CompletableFuture是为构建DAG结构的操作管道而设计的,类似于Java .简洁地描述数据流DAG的一个节点的中断如何影响DAG其余部分的执行是非常困难的。(当任何节点被中断时,是否应该立即取消所有并发任务?)
  4. 考虑到现在JDK和库的内部复杂性,我怀疑JDK团队只是不想处理中断问题。( lambda系统的内部部件--呃.)

一种非常麻烦的方法是让每个CompletableFuture导出对外部可见AtomicReference的引用,然后在需要时从另一个外部线程直接中断Thread引用。或者,如果您使用自己的ExecutorService启动所有任务,在您自己的ThreadPool中,您可以手动中断任何或所有已启动的线程,即使CompletableFuture拒绝通过cancel(true)触发中断。(请注意,尽管CompletableFuture lambda不能抛出选中的异常,所以如果CompletableFuture中有可中断的等待,则必须作为未检查的异常重新抛出。)

更简单地说,您只需在外部作用域中声明一个AtomicReference<Boolean> cancel = new AtomicReference<>(),并定期从每个CompletableFuture任务的lambda内部检查此标志。

您还可以尝试设置Future实例的DAG,而不是CompletableFuture实例的DAG,这样您就可以准确地指定一个任务中的异常和中断/取消如何影响另一个当前正在运行的任务。I show how to do this in my example code in my question here,它工作得很好,但它有很多样板。

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23320407

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档