在 CompletableFuture 中使用 Supplier 会产生与使用 lambda 不同的结果

using Supplier in CompletableFuture yields different result than using lambda

我创建了一个读取文本文件的小示例,并用 CompletableFuture 包装调用。

public class Async {
    public static void main(String[] args) throws Exception {
        CompletableFuture<String> result = ReadFileUsingLambda(Paths.get("path/to/file"));
        result.whenComplete((ok, ex) -> {
            if (ex == null) {
                System.out.println(ok);
            } else {
                ex.printStackTrace();
            }
        });
    }

    public static CompletableFuture<String> ReadFileUsingSupplier(Path file) throws Exception {
        return CompletableFuture.supplyAsync(new Supplier<String>() {
            @Override
            public String get() {
                try {
                    return new String(Files.readAllBytes(file));
                } catch (IOException e) {
                    e.printStackTrace();
                    return "test";
                }
            }
        }, ForkJoinPool.commonPool());
    }

    public static CompletableFuture<String> ReadFileUsingLambda(Path file) throws Exception {
        return CompletableFuture.supplyAsync(() -> {
            try {
                return new String(Files.readAllBytes(file));
            } catch (IOException e) {
                e.printStackTrace();
                return "test";
            }
        } , ForkJoinPool.commonPool());
    }
}

这段代码returns没什么。它执行 "nothing happens",没有错误或输出。如果我调用 ReadFileUsingSupplier 而不是 ReadFileUsingLambda 那么我会在控制台中打印文件内容!

对我来说这没有意义,因为 lambda 是用于编写内联函数的 shorthand,它不应该改变行为,但在这个例子中它显然改变了。

我认为这只是执行时间的问题 - lambda 可能需要更多的时间来执行,允许程序在您完成读取文件之前退出。

试试这个:

  • ReadFileUsingSupplier 中的 try 块中添加一个 Thread.sleep(1000); 作为第一条语句,您将看不到任何输出
  • 在使用 ReadFileUsingLambda 时在 main 的末尾添加一个 Thread.sleep(1000);,您将看到预期的输出

为确保您的 main 不会在 future 完成之前退出,您可以调用:

result.join();

如前所述,在任何一种情况下都需要 result.join() 以避免主线程退出得太快。

似乎在 JVM 预热时使用 lambda 与匿名闭包相比会受到惩罚,此后性能相同。我在 another SO thread - which in turn links a performance study by Oracle.

上找到了此信息

作为旁注,Thread.sleep() 永远都不是解决奇怪的计时问题的好主意。当您或其他人重新阅读时,找出原因并采取适当的措施会更加清楚,例如

System.out.println(result.get(5, TimeUnit.SECONDS));

这也使您能够放弃 .join()。