当前位置: 首页 > 面试题库 >

CompletableFuture的完成处理程序在哪个线程中执行?

澹台成龙
2023-03-14
问题内容

我对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用于计划和撰写任务的人来说,这些都是潜在的重要点。考虑以下事件序列:

  1. 线程A通过注册一个相关的完成f.thenApply(c1)
  2. 一段时间后,线程B调用f.complete()
  3. 大约在同一时间,线程C通过注册另一个依赖完成f.thenApply(c2)

从概念上讲,complete()它做两件事:发布将来的结果,然后尝试调用相关的完成。现在,如果线程C 在发布结果值之后但在线程B开始调用之前运行,该c1怎么办?根据实现的不同,线程C可能会看到f已经完成,然后可以调用c1 和 c2。或者,线程C可以调用,c2而线程B可以调用c1。该文档不排除任何可能性。考虑到这一点,以下是文档不支持的假设:

  1. 在调用之前将调用在完成之前c注册的从属完成。f f.complete()
  2. c到时间f.complete()回来,那将完成;
  3. 依赖完成将以任何特定顺序(例如注册顺序)被调用;
  4. 完成之前 注册的相关完成f将在完成之后 注册的f完成之前调用。

考虑另一个示例

  1. 线程A调用f.complete();
  2. 一段时间后,线程B通过f.thenApply(c1); 注册完成。
  3. 大约在同一时间,线程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方法有疑问: 事情是JavaDoc这么说的: 返回一个新的CompletionStage,当此阶段正常完成时,将使用该阶段的结果作为所提供函数的参数来执行该阶段。有关涵盖异常完成的规则​​,请参见CompletionStage文档。 那线程呢?这将在哪个线程中执行?如果将来由线程池完成怎么办? 问题答案: 文档中指定的策略可以帮助您更好地理解: 对

  • 我有一个关于CompletableFuture方法的问题: JavaDoc的意思是: 返回一个新的完成阶段,当此阶段正常完成时,将使用此阶段的结果作为所提供函数的参数来执行该阶段。有关异常完成的规则,请参阅完成阶段文档。 穿线呢?这将在哪个线程中执行?如果未来由线程池来完成呢?

  • 我是spring batch的新手。我已经使用多个线程从spring创建并成功执行了作业,它工作得很好,只是当程序执行完成时,程序流不会结束/停止。i、 即使main方法的最后一条语句被执行,程序也不会退出。我不确定它是否一直在等待线程完成,或者是什么。有人能给我一些建议吗?“下面是我的作业配置文件 下面是启动器代码 如上所述,代码在5个不同的线程中为任务“hello”运行,为任务“world”运

  • 在阅读关于流的留档时,我遇到了以下句子: > 。。。试图从行为参数访问可变状态会给您带来错误的选择。。。如果您不同步对该状态的访问,您将面临数据竞争,因此您的代码将被破坏。。。[1] 如果行为参数确实有副作用。。。[没有]保证在同一个线程中执行同一流管道中“相同”元素上的不同操作。[2] 对于任何给定的元素,可以在库选择的任何时间和线程中执行该操作。[3] 这些句子没有区分顺序流和平行流。因此,我

  • 我目前正在学习swift,几个小时以来我一直在试图理解一个错误。由于查询,我试图从php脚本中获取一个xml文件,我使用一个完成处理程序作为回调来获取这些查询的结果。 问题是我对这些技术感到不舒服,它说缺少论据。 下面是使用完成处理程序的函数: 这是我调用这个函数的一段代码: 这段代码显示有关调用连接的此错误: 我做错了什么?为什么它要两个参数? 非常感谢您的帮助。 埃尔巴托

  • 问题内容: 我有2个线程(线程1和线程2)。而且我有信号处理。每当发生线程2时,都应处理该信号。为此,我写了下面的程序 我编译并运行该程序。每1秒打印一次“ thread1 active”,每3秒打印一次“ thread2 active”。 现在我生成了。但是它会像上面那样显示“ thread1 active”和“ thread2 active”消息。再次生成了,现在每3秒仅打印一次“ threa