在 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()。
我创建了一个读取文本文件的小示例,并用 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()。