CompletableFuture的完成处理程序在哪个线程中执行?
我对CompletableFuture方法有疑问:
public <U> CompletableFuture<U> thenApply(Function<? super T, ? extends U> fn)
事情是JavaDoc这么说的:
返回一个新的CompletionStage,当此阶段正常完成时,将使用该阶段的结果作为所提供函数的参数来执行该阶段。有关涵盖异常完成的规则,请参见CompletionStage文档。
那线程呢?这将在哪个线程中执行?如果将来由线程池完成怎么办?
回答:
CompletableFuture
文档中指定的策略可以帮助您更好地理解:
对于非异步方法依赖完井供给动作可以由执行 该完成当前CompletableFuture线程 , 或者通过完成方法的任何其他呼叫者 。
所有没有显式Executor参数的异步方法都使用来执行
ForkJoinPool.commonPool()
(除非它不支持并行度至少为2,在这种情况下,将创建一个新的Thread来运行每个任务
)。为了简化监视,调试和跟踪,所有生成的异步任务都是标记接口的实例
CompletableFuture.AsynchronousCompletionTask
。
:我也建议阅读@Mike的答案,作为对文档详细信息的有趣分析。
正如@nullpointer指出的那样,文档会告诉您您需要了解的内容。但是,相关文本令人惊讶地模糊不清,此处发布的某些评论(和答案)似乎依赖于文档不支持的假设。因此,我认为有必要将其分开。具体来说,我们应该非常仔细地阅读本段内容:
为非异步方法的相关完成提供的动作可以由完成当前CompletableFuture的线程执行,也可以由完成方法的任何其他调用者执行。
听起来很简单,但是细节上却很少。似乎故意避免描述何时可以在完成线程上调用依赖完成,而不是在调用诸如的完成方法期间thenApply
。如前所述,以上段落实际上是在恳求我们用假设来填补空白。这很危险,特别是当主题涉及并发和异步编程时,当我们随着程序员的发展而产生的许多期望被抛诸脑后。让我们仔细看一下文档中没有说的内容。
该文档没有声称在调用之前注册的相关完成complete()
将在完成线程上运行。而且,尽管它声明在调用诸如的完成方法时可能会调用从属补全thenApply,但并未声明将在注册它的线程上调用补全(注意单词“ any other”
)。
对于任何CompletableFuture
用于计划和撰写任务的人来说,这些都是潜在的重要点。考虑以下事件序列:
- 线程A通过注册一个相关的完成
f.thenApply(c1)
。 - 一段时间后,线程B调用
f.complete()
。 - 大约在同一时间,线程C通过注册另一个依赖完成
f.thenApply(c2)
。
从概念上讲,complete()它做两件事:发布将来的结果,然后尝试调用相关的完成。现在,如果线程C 在发布结果值之后但在线程B开始调用之前运行,该c1怎么办?根据实现的不同,线程C可能会看到f已经完成,然后可以调用c1 和 c2。或者,线程C可以调用,c2而线程B可以调用c1。该文档不排除任何可能性。考虑到这一点,以下是文档不支持的假设:
- 在调用之前将调用在完成之前
c
注册的从属完成。f f.complete()
- c到时间
f.complete()
回来,那将完成; - 依赖完成将以任何特定顺序(例如注册顺序)被调用;
- 完成之前 注册的相关完成f将在完成之后 注册的f完成之前调用。
考虑另一个示例:
- 线程A调用
f.complete()
; - 一段时间后,线程B通过f.thenApply(c1); 注册完成。
- 大约在同一时间,线程C通过分别注册一个完成
f.thenApply(c2)
。
如果知道f已经完成,可以尝试假设c1会在期间调用f.thenApply(c1)
和c2会在期间调用f.thenApply(c2)
。人们可能会进一步假设,c1到时间f.thenApply(c1)
返回时,它已经完成了。但是,文档不支持这些假设。它可能是一个线程,呼吁thenApply
各上调调用既 c1和c2,而其他线程调用都不是。
仔细分析JDK代码可以确定上面的假设方案如何发挥作用。但这甚至是有风险的,因为您最终可能会依赖于(1)不可移植或(2)可能会更改的实现细节。最好的选择是不要假设javadocs或原始JSR规范中未阐明的任何内容。
tldr:谨慎假设,编写文档时,请尽可能清晰明了。简洁是一件奇妙的事情,但请注意人类填补空白的趋势。
以上是 CompletableFuture的完成处理程序在哪个线程中执行? 的全部内容, 来源链接: utcz.com/qa/414593.html