当前位置: 首页 > 知识库问答 >
问题:

为什么即使我没有调用get()或join(),这个CompletableFuture也能工作呢?

刘琨
2023-03-14

我在研究完全未来的时候有一个问题。 方法正在阻止调用。如果我不给他们两个打电话呢?

此代码调用 :

// 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!

此代码既不调用 ,也不调用 :

// 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的runnable块工作。

共有3个答案

邢献
2023-03-14

第二种情况是“工作”,因为您使主线程休眠的时间足够长(5秒)。工作是在引号之间,因为它不是真正的工作,只是完成。我假设这里的代码应该输出 ,以便被认为“正常工作”。

在这两种情况下,在主线程的末尾尝试相同的代码,使用此睡眠时间:

1.第一个将以相同的方式运行,因为get操作阻塞主线程。事实上,对于第一种情况,你甚至不需要最后的睡眠时间。

输出:

2.第二种情况不会输出 ,因为没有人告诉主线程:“嘿,等待这个完成”。这就是 所做的:阻止调用方以等待任务完成。如果没有它,并在结束时设置低睡眠时间,则会调用runnable,但在主线程停止之前无法完成其工作。

输出:

这也是为什么在第一种情况下, (首先是runnable的输出,然后是main的输出--意味着主线程被阻塞,直到 返回)被写入,而第二种情况显示出阅读障碍的细微迹象:

但它不是诵读困难症,它只是执行命令。在第二种情况下,会发生这种情况:

1.调用runnable。

2.主线程继续其进程,打印(World!)

3.设置 时间:运行时1秒/主时5秒。(Runnable's sleep也可以在第二步执行,但我把它放在这里是为了澄清行为)

4.runnable任务在1秒后打印(Hello“),CompletableFuture完成。

5.5秒过去了,主线程停止。

因此runnable可以打印 ,因为它能够在这5秒超时之间执行命令。

World! . . . . . .(1)Hello. . . . . . . . . . .(5)[END]

如果将最后5秒的超时时间减少到0.5秒,则会得到

World!. . (0.5)[END]
卫念
2023-03-14

我不知道为什么case2的 块工作。

它没有理由不起作用。

方法表示异步执行任务。假设应用程序没有过早结束,无论您是否等待它完成,任务最终都会完成。

提供了等待任务完成的各种方法。但在您的示例中,您并没有为此目的使用它。相反,main方法中的 调用具有相同的效果;即等待的时间足够长,以至于任务(可能)已经完成。因此 之前输出。

重申一下, 调用不会导致任务发生。相反,它等待它的发生。

使用 来等待事件(例如任务的完成)的发生是个坏主意:

    即使在本例中,理论上main中的 也有可能在任务中的 之前完成sup1/sup>。

    基本上, 的目的是提供一种有效的方法来等待任务完成并交付结果。你应该用它…。

    来举例说明。您的应用程序在输出 之间等待(并且浪费)4秒。如果您按照预期使用 ,您就不会有这4秒的“死时间”。

蓬英逸
2023-03-14

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

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

但是实际调用 时, 可能已经完成。 只是阻塞,直到所有阶段都完成,它不会触发计算;计算被立即安排。

一个小的补充是,您可以完成一个 ,而不需要等待管道的执行完成:例如 (这一个设置它,即使它已经完成)等等。下面是一个有趣的例子: ionally>

 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

所以即使工作开始了,最终的值也是 ,而不是

 类似资料:
  • 我希望在屏幕旋转期间保留一个复杂的java对象,因此我使该对象可打包并实现了必要的方法: 在writeToParcel(Parcel dest,int flags)方法中,我将一些值保存到“dest”。 在Parcelable.Creator的createFromParcel(Parcel source)方法中,我以正确的顺序从“source”中获取了值,并返回了相应的对象。 然后在Fragmen

  • 我有一个功能,会显示其他内容和隐藏最近的内容。但当我单击submit输入时,它就不工作了。我尝试使用锚标记和按钮标记,它有相同的输出。 HTML: null null 我尝试使用和以及,但没有发生任何事情。我也找到了一个答案,但不像我的问题。

  • 下面是我的代码: 这两种方法我都试过了,但我看不出结果有什么不同。

  • 这是我的线程: 它得到一个上限和下限以及一个在所有线程上共享的结果。 这是我的测试类:

  • chrome浏览器 onclick链接 O:https://www.w3schools.com窗口打开 X:about:空白窗口打开

  • 我有这个项目。然后我做以下步骤: 转到 预期结果:错误消息(在浏览器中呈现) 白标签错误页 此应用程序没有 /error的显式映射,因此您将此视为一种退回。星期二八月27 16:59:23CEST 2019有一个意外的错误(类型=未找到,状态=404)。没有可用的消息 如何更改附加的代码,使文件索引代替此错误。是否呈现xhtml? 更新1:如果我去,我得到这个错误: 白标签错误页此应用程序没有/E

  • 为什么这不会增加?我正在批处理文件中使用这个。它做任何事情都是正确的,只是没有增加。我做错了什么? 编辑:这不是重复,这是一个完全不同的问题。

  • 我在应用程序中实现了firebase,以便使用它的通知服务,它工作了,今天决定使用它的键/值特性,但意识到无论我在从继承的类中编写什么,都不会发生任何事情。所以我决定移除my类以达到dubugging的目的,但我还是收到了通知。所以我从清单中删除了firebase消息传递服务,删除了应用缓存并运行了项目,但我仍然得到通知!我能阻止通知到来的唯一方法是移除Gradle中的firebase依赖项。这里