首页 > 解决方案 > 为什么即使我不调用 get() 或 join(),这个 CompletableFuture 也能工作?

问题描述

我在学习时有一个问题CompletableFuture。/方法是阻塞调用get()join()如果我不打电话给他们怎么办?

此代码调用get()

// Case 1 - Use get()
CompletableFuture.runAsync(() -> {
    try {
        Thread.sleep(1_000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Hello");
}).get();
System.out.println("World!");

Thread.sleep(5_000L); // Don't finish the main thread

输出:

Hello
World!

此代码既不调用get()也不调用join()

// Case 2 - Don't use get()
CompletableFuture.runAsync(() -> {
    try {
        Thread.sleep(1_000L);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("Hello");
});
System.out.println("World!");

Thread.sleep(5_000L); // For don't finish main thread

输出:

World!
Hello

我不知道为什么案例 2 的可运行块正在工作。

标签: javamultithreadingcompletable-future

解决方案


的整个想法CompletableFuture是立即安排它们启动(尽管您无法可靠地判断它们将在哪个线程中执行),并且当您到达getorjoin时,结果可能已经准备好,即:CompletableFuture可能已经完成。在内部,一旦管道中的某个阶段准备就绪,该特定阶段CompletableFuture将被设置为完成。例如:

String result = 
   CompletableFuture.supplyAsync(() -> "ab")
                    .thenApply(String::toUpperCase)
                    .thenApply(x -> x.substring(1))
                    .join();

与以下内容相同:

CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "ab");
CompletableFuture<String> cf2 = cf1.thenApply(String::toUpperCase);
CompletableFuture<String> cf3 = cf2.thenApply(x -> x.substring(1));
String result = cf3.join();

当您到达实际调用join时,cf3可能已经完成。get并且join只是阻塞直到所有阶段完成,它不会触发计算;立即安排计算。


一个小的补充是您可以在CompletableFuture不等待管道执行完成的情况下完成 a :例如complete, completeExceptionally, obtrudeValue(即使它已经完成,也可以设置它)obtrudeExceptioncancel. 这是一个有趣的例子:

 public static void main(String[] args) {
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() -> {
        System.out.println("started work");
        LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(5));
        System.out.println("done work");
        return "a";
    });

    LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
    cf.complete("b");
    System.out.println(cf.join());
}

这将输出:

started work
b

所以即使工作开始了,最终的价值是b,不是a


推荐阅读