将阻塞方法转换为异步

根据你的连接,以下方法将花费一秒或两秒来检索网页并计算文本长度。无论什么线程调用它都会阻塞那段时间。它也会重新抛出一个稍后有用的异常。

public static long blockingGetWebPageLength(String urlString) {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(new URL(urlString).openConnection().getInputStream()))) {
        StringBuilder sb = new StringBuilder();
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line);
        }
        return sb.toString().length();
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

这将它转换为一个方法,通过将阻塞方法调用移动到另一个线程立即返回。默认情况下,supplyAsync 方法将在公共池上运行供应商。对于阻塞方法,这可能不是一个好选择,因为可能会耗尽该池中的线程,这就是我添加可选服务参数的原因。

static private ExecutorService service = Executors.newCachedThreadPool();

static public CompletableFuture<Long> asyncGetWebPageLength(String url) {
    return CompletableFuture.supplyAsync(() -> blockingGetWebPageLength(url), service);
}

要以异步方式使用该函数,应该使用接受 lamda 的方法,当它完成时接受供应商的结果,例如 thenAccept。此外,使用 exceptionly 或 handle 方法记录可能发生的任何异常也很重要。

public static void main(String[] args) {

    asyncGetWebPageLength("https://stackoverflow.com/")
            .thenAccept(l -> {
                System.out.println("Stack Overflow returned " + l);
            })
            .exceptionally((Throwable throwable) -> {
                Logger.getLogger("myclass").log(Level.SEVERE, "", throwable);
                return null;
            });

}