Java Spring boot@Async方法实际上是如何异步/非阻塞的?
以下示例取自 这实际上会阻塞它将阻止Java Spring boot@Async方法实际上是如何异步/非阻塞的?,java,spring,spring-boot,completable-future,spring-async,Java,Spring,Spring Boot,Completable Future,Spring Async,以下示例取自 这实际上会阻塞它将阻止Executor服务上的另一个线程,但它将由于线程而被阻止。sleep(1000L)正确吗? 那么这是如何异步的呢 我的意思是,CompletableFuture的全部要点是获取对未来将完成的计算的引用但是在这里,当我得到完整的未来时,计算已经结束了,即,我们正在使用CompletableFuture.completedFuture(结果) 那么,在这种情况下,拥有一个完整的未来有什么意义呢我的意思是,如果我只在计算结束并得到结果时才阻塞并返回,那么我最好只返
Executor
服务上的另一个线程,但它将由于线程而被阻止。sleep(1000L)
正确吗?
那么这是如何异步的呢
我的意思是,CompletableFuture
的全部要点是获取对未来将完成的计算的引用但是在这里,当我得到完整的未来时,计算已经结束了,即,我们正在使用CompletableFuture.completedFuture(结果)
那么,在这种情况下,拥有一个完整的未来有什么意义呢我的意思是,如果我只在计算结束并得到结果时才阻塞并返回,那么我最好只返回结果,而不是CompletableFuture
这是如何真正实现非阻塞/异步的
我在这里发现的唯一一个非阻塞方面是卸载到另一个线程,而不是别的。
我哪里出了问题吗?我错过了什么
谢谢。问题在于你如何创造你的
未来。您使用的代码是
CompletableFuture.completedFuture(results)
引用,这只是同步和异步之间的包装,其中计算是同步完成的:
返回已使用给定值完成的新CompletableFuture
这在某些情况下非常有用,因为您只希望对某些输入执行异步工作。考虑
(x) -> x==0 ? CompletableFuture.completedFuture(0) : CompletableFuture.supplyAsync(expensiveComputation)
我希望这能让区别变得清晰-如果您想要真正的异步计算,您需要使用supplyAsync
函数:
返回一个新的CompletableFuture
,该任务由运行在ForkJoinPool.commonPool()
中的任务异步完成,其值通过调用给定的供应商获得
您缺少的细节是,当使用@Async
时(并正确配置),将使用代理bean包装您的服务bean。通过代理对异步方法的任何调用都将使用Spring异步运行该方法
将方法响应包装在一个同步的Future
中,例如CompletableFuture。completedFuture
是必需的,因此返回类型可以是Future
。但是,您返回的Future
不是代理返回的。相反,代理返回由TaskExecutor
提供的Future
,它将被异步处理。您通过创建的Future
,例如CompletableFuture.completedFuture
由代理展开,其完成结果由代理的Future
返回
代理文档
我没有看到在或或Javadocs中明确说明的所有上述代理细节。但是,可以通过阅读所提供内容的字里行间来拼凑细节
@Async
Javadocs顺便提到了服务代理,并解释了在服务方法的实现中使用CompletableFuture.completedFuture
的原因:
从代理返回的Future
句柄将是一个实际的异步Future
,可用于跟踪异步方法执行的结果。但是,由于目标方法需要实现相同的签名,因此它必须返回一个临时的Future
句柄,该句柄只传递一个值:例如Spring的AsyncResult
、EJB3.1的AsyncResult
、或CompletableFuture.completedFuture(对象)
代理涉及的事实也很明显,因为两个@EnableAsync
注释元素指定代理细节:和
问题示例
最后,将此应用于问题示例将使其具体化。在GitHubLookupService
bean上调用findUser
方法的代码实际上是在代理类上调用方法,而不是直接在GitHubLookupService
实例上调用方法。代理类的findUser
方法将任务提交给Spring的TaskExecutor
,并返回一个CompletableFuture
,该任务将在提交的任务完成时异步完成
提交的任务将调用非代理GitHubLookupService
中的实际findUser
方法。这将执行REST调用,休眠1秒,并返回一个完整的CompletableFuture
,其中包含REST结果
由于此任务发生在Spring的TaskExecutor
创建的单独线程中,因此调用代码将继续立即通过GitHubLookupService.findUser
调用,即使它至少需要1秒才能返回
如果调用代码中使用了findUser
调用的结果(例如使用),则从Future
中获得的值将与results
中传递给CompletableFuture.completedFuture
的值相同。我没有在这里编写代码。我是从Spring文档中得到的。这是不准确的,因为Spring代理魔法正在幕后进行:。在这个问题中使用的CompletableFuture.completedFuture(results)
是惯用的Spring@Async
方法,最终结果仍然是一个异步调用。
CompletableFuture.completedFuture(results)
(x) -> x==0 ? CompletableFuture.completedFuture(0) : CompletableFuture.supplyAsync(expensiveComputation)