了解 Java 的 Completable Future 的行为
Understanding behaviour of Java's Completable Future
我正在学习 Java,我有一个相对简单的 Java 程序,它从 API 端点获取数据,如下所示:
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
cf.thenAcceptAsync(System.out::println);
// System.out.println(cf.join()); <=== Commenting out this line
}
}
我希望上面的代码打印出原始的 JSON,但它什么也没做。但是,如果我包含上面已被注释掉的行,代码可以工作,但它会打印出原始 JSON 两次。
我的猜测是程序在 thenAcceptAsync
有机会完成之前终止,当包含阻塞 .join()
函数时情况并非如此。我的猜测是否正确,如果正确,我该如何解决这个问题?
您的主线程没有等待服务调用的完成。您应该在 CompletableFuture 上调用 join 以等待其执行完成:
cf.thenAcceptAsync(System.out::println).join();
您可以使用以下修改后的代码版本检查行为(只需添加一个关闭挂钩以在 VM 退出时打印文本):
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Shutting down")));
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
System.out.println("running...");
String result = getJSON("https://jsonplaceholder.typicode.com/posts/1");
System.out.println("Completed service call");
return result;
});
cf.thenAcceptAsync(System.out::println).join();
当你运行以上代码时,输出如下:
running...
Completed service call
{ "result json here"}
Shutting down
但是,如果没有 .join()
,则会立即出现以下输出:
电子
运行宁...
正在关机
简而言之,thenAcceptAsync(System.out::println)
returns 立即完成并且主线程完成,在本例中是在 HTTP 调用完成之前。如果在那之后你有工作要做,它会喜欢:
cf = cf.thenAcceptAsync(System.out::println);
doSomethingElse();
doYetAnotherThing();
cf.join()
join
最终应该被调用,以防止 VM 过早终止,或者在必要时等待结果准备就绪。
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
//cf.thenAcceptAsync(System.out::println);
System.out.println(cf.join());
}
}
只需注释并在行下方打开它只会打印一行
我正在学习 Java,我有一个相对简单的 Java 程序,它从 API 端点获取数据,如下所示:
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
cf.thenAcceptAsync(System.out::println);
// System.out.println(cf.join()); <=== Commenting out this line
}
}
我希望上面的代码打印出原始的 JSON,但它什么也没做。但是,如果我包含上面已被注释掉的行,代码可以工作,但它会打印出原始 JSON 两次。
我的猜测是程序在 thenAcceptAsync
有机会完成之前终止,当包含阻塞 .join()
函数时情况并非如此。我的猜测是否正确,如果正确,我该如何解决这个问题?
您的主线程没有等待服务调用的完成。您应该在 CompletableFuture 上调用 join 以等待其执行完成:
cf.thenAcceptAsync(System.out::println).join();
您可以使用以下修改后的代码版本检查行为(只需添加一个关闭挂钩以在 VM 退出时打印文本):
Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Shutting down")));
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
System.out.println("running...");
String result = getJSON("https://jsonplaceholder.typicode.com/posts/1");
System.out.println("Completed service call");
return result;
});
cf.thenAcceptAsync(System.out::println).join();
当你运行以上代码时,输出如下:
running...
Completed service call
{ "result json here"}
Shutting down
但是,如果没有 .join()
,则会立即出现以下输出:
电子
运行宁...
正在关机
简而言之,thenAcceptAsync(System.out::println)
returns 立即完成并且主线程完成,在本例中是在 HTTP 调用完成之前。如果在那之后你有工作要做,它会喜欢:
cf = cf.thenAcceptAsync(System.out::println);
doSomethingElse();
doYetAnotherThing();
cf.join()
join
最终应该被调用,以防止 VM 过早终止,或者在必要时等待结果准备就绪。
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
public class Main {
public static String getJSON(String u) {
if (u == null) throw new IllegalArgumentException("URL is null.");
try {
URL url = new URL(u);
URLConnection site = url.openConnection();
InputStream is = site.getInputStream();
Scanner scanner = new Scanner(
new BufferedInputStream(is),
"UTF-8");
String resp = "";
while (scanner.hasNextLine()) {
resp = resp + scanner.nextLine();
}
return resp;
} catch (Exception e) {
System.out.println(e);
return null;
}
}
public static void main(String[] args) {
CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->
getJSON("https://jsonplaceholder.typicode.com/posts/1")
);
//cf.thenAcceptAsync(System.out::println);
System.out.println(cf.join());
}
}
只需注释并在行下方打开它只会打印一行