了解Java的Completable Future的行为

时间:2018-05-24 11:57:09

标签: java asynchronous concurrency java-8 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()函数时则不然。我的猜测是正确的,如果是的话,我该如何解决这个问题?

2 个答案:

答案 0 :(得分:3)

您的主要线程没有等待服务电话的完成。您应该在CompletableFuture上调用join以等待其执行完成:

TestContext

您可以使用以下修改版本的代码检查行为(只需在VM退出时添加关闭挂钩以打印文本):

cf.thenAcceptAsync(System.out::println).join();

运行上面的代码时,输​​出以下内容:

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()立即返回,主线程完成,在这种情况下,在HTTP调用完成之前。如果你之后有工作要做,那就像是:

thenAcceptAsync(System.out::println)
最终应该调用

cf = cf.thenAcceptAsync(System.out::println); doSomethingElse(); doYetAnotherThing(); cf.join() ,以防止过早终止VM,或者在必要时等待结果准备好。

答案 1 :(得分:1)

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()); 
  }

}

只需评论并在下面打开它就会打印一行