我有两个技术上相同的代码片段,但第二个代码片段比第一个片段多1秒。第一个在6秒内执行,第二个在7秒内执行。
Double yearlyEarnings = employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId());
CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenApplyAsync(currencyConv -> {
return currencyConv * yearlyEarnings;
});
上面的一个需要6s而下一个需要7s Here is the link to code
CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenApplyAsync(currencyConv -> {
Double yearlyEarnings = employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId());
return currencyConv * yearlyEarnings;
});
请解释为什么第二个代码与第一个代码相比持续多花了1秒(额外时间)
以下是 getYearlyEarningForUserWithEmployer 方法的签名。只是分享,但它不应该有任何影响
Double getYearlyEarningForUserWithEmployer(long userId, long employerId);
答案 0 :(得分:0)
你的问题非常不完整,但根据我们的猜测,如果我们假设currencyConvCF
表示可能在执行代码片段时同时运行的异步操作,那么第二个变体需要更长的时间是完全合理的而且你在谈论完成所有操作所需的总时间,包括由CompletableFuture
(thenApplyAsync
)返回的earlyEarningsInHomeCountryCF
代表的操作。
在第一个变体中,您调用getYearlyEarningForUserWithEmployer
,而currencyConvCF
表示的操作可能仍在同时运行。两次操作完成后都会发生乘法。
在第二个变体中,getYearlyEarningForUserWithEmployer
调用是传递给currencyConvCF.thenApplyAsync
的操作的一部分,因此它不会在currencyConvCF
表示的操作完成之前启动,因此无操作将同时运行。如果我们假设getYearlyEarningForUserWithEmployer
需要很长时间,比如说一秒钟,并且没有内部依赖关系到另一个操作,那么当整个操作在该变体中花费更长时间时就不足为奇了。
看来,你真正想要的是:
CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenCombineAsync(
CompletableFuture.supplyAsync(
() -> employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId())),
(currencyConv, yearlyEarnings) -> currencyConv * yearlyEarnings);
所以getYearlyEarningForUserWithEmployer
不会在启动线程中按顺序执行,但是两个源操作都可以在最终乘法应用之前异步运行。
但是,当您在启动线程中调用get
之后,就像在github上的链接代码中那样,第二次操作的异步处理没有任何好处。您的启动线程可以只执行独立操作,而不是等待完成,因为您的问题的第二个代码变量已经执行了,当您不为单个乘法这样简单的事情生成异步操作时,您可能会更快,即使用代替:
CompletableFuture<Double> currencyConvCF = /* a true asynchronous operation */
return employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId())
* employerCurrencyCF.join();
答案 1 :(得分:0)
Holger所说的确有道理,但不是我发布的问题。我同意这个问题不是以最好的方式写的。
问题在于,期货的编写顺序导致时间的持续增加。
理想情况下,只要代码以正确的反应方式编写,未来的顺序就无关紧要了
问题的原因是Java的默认ForkJoinPool和Java默认使用此池来运行所有CompletableFutures。如果我使用自定义池运行所有CompletableFutues,我几乎会得到相同的时间,而不管未来语句的编写顺序如何。
我仍然需要找到ForkJoinPool的限制,并找出为什么我的20个线程的自定义池表现更好。
当我找到正确的理由时,我会更新我的答案。