-3

我有两个技术上相同的代码片段,但是第二个比第一个多花 1 秒。第一个在 6 秒内执行,第二个在 7 秒内执行。

Double yearlyEarnings = employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId());

CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenApplyAsync(currencyConv -> {
  return currencyConv * yearlyEarnings;
});

上一个需要 6s,下一个需要 7s 这是代码的链接

CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenApplyAsync(currencyConv -> {
       Double yearlyEarnings = employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId());
       return currencyConv * yearlyEarnings;  
 });

请解释为什么与第一个代码相比,第二个代码始终多花费 1 秒(额外时间)

下面是方法getYearlyEarningForUserWithEmployer的签名。只是分享,但应该不会有任何影响

Double getYearlyEarningForUserWithEmployer(long userId, long employerId);

这是代码的链接

4

2 回答 2

0

您的问题非常不完整,但是根据我们的猜测,如果我们假设第二个变体currencyConvCF代表一个异步操作,该操作可能在您的代码片段执行时同时运行并且您正在谈论整体完成所有操作所需的时间,包括由( )CompletableFuture返回的操作。thenApplyAsyncearlyEarningsInHomeCountryCF

在您调用的第一个变体中getYearlyEarningForUserWithEmployer,由 表示的操作currencyConvCF可能仍在同时运行。当两个操作都完成时,将发生乘法。

在第二个变体中,getYearlyEarningForUserWithEmployer调用是传递给 的操作的一部分currencyConvCF.thenApplyAsync,因此它不会在由 表示的操作currencyConvCF完成之前启动,因此不会同时运行任何操作。如果我们假设这getYearlyEarningForUserWithEmployer需要很长时间,比如一秒钟,并且与其他操作没有内部依赖关系,那么在该变体中整个操作需要更长的时间也就不足为奇了。

看来,您实际上想要做的是:

CompletableFuture<Double> earlyEarningsInHomeCountryCF = currencyConvCF.thenCombineAsync(
    CompletableFuture.supplyAsync(
        () -> employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId())),
    (currencyConv, yearlyEarnings) -> currencyConv * yearlyEarnings);

sogetYearlyEarningForUserWithEmployer不会在启动线程中按顺序执行,但两个源操作可以在最终乘法应用之前异步运行。

但是,当您get随后在启动线程中调用时,例如在 github 上的链接代码中,第二个操作的异步处理没有任何好处。而不是等待完成,您的启动线程可以像您的问题的第二个代码变体已经执行的那样执行独立操作,并且当不为像单次乘法这样简单的事情产生异步操作时,您可能会更快,即使用反而:

CompletableFuture<Double> currencyConvCF = /* a true asynchronous operation */
return employmentService.getYearlyEarningForUserWithEmployer(userId, emp.getId())
     * employerCurrencyCF.join();
于 2016-03-29T18:11:28.427 回答
0

霍尔格所说的确实有道理,但在我发布的问题中却没有。我同意这个问题不是以最好的方式写的。

问题在于,期货的编写顺序导致时间持续增加。

理想情况下,只要代码以正确的反应方式编写,未来的顺序应该无关紧要

问题的原因是 Java 的默认 ForkJoinPool 和 Java 默认使用这个池来运行所有 CompletableFutures。如果我使用自定义池运行所有 CompletableFutues,我得到的时间几乎是相同的,而不管未来语句的编写顺序如何。

我仍然需要找出 ForkJoinPool 的限制是什么,并找出为什么我的 20 个线程的自定义池性能更好。

当我找到正确的原因时,我会更新我的答案。

于 2016-04-04T01:53:05.460 回答