Java 为什么即使我没有';t调用get()或join()?
我在学习Java 为什么即使我没有';t调用get()或join()?,java,multithreading,completable-future,Java,Multithreading,Completable Future,我在学习CompletableFuture时遇到了一个问题。get()/join()方法正在阻止调用。如果我不给他们打电话怎么办 此代码调用get(): 输出: Hello World! World! Hello 此代码既不调用get()也不调用join(): 输出: Hello World! World! Hello 我不知道为什么案例2的runnable块工作。第二个案例“工作”,因为您睡眠主线程的时间足够长(5秒)。工作是在引号之间,因为它不是真正的工作,只是完成。我在这里假设代码
CompletableFuture
时遇到了一个问题。get()
/join()
方法正在阻止调用。如果我不给他们打电话怎么办
此代码调用get()
:
输出:
Hello
World!
World!
Hello
此代码既不调用get()
也不调用join()
:
输出:
Hello
World!
World!
Hello
我不知道为什么案例2的runnable块工作。第二个案例“工作”,因为您睡眠主线程的时间足够长(5秒)。工作是在引号之间,因为它不是真正的工作,只是完成。我在这里假设代码应该输出helloworld代码>以被视为“正常工作”
在这两种情况下,在主线程结束时使用此睡眠时间尝试相同的代码:
Thread.sleep(100)代码>
1。第一个线程的行为方式相同,因为get操作阻塞了主线程。事实上,对于第一种情况,你甚至不需要最后的睡眠时间
输出:你好,世界代码>
2。第二个案例不会输出Hello
,因为没有人告诉主线程:“嘿,等这个完成”。这就是get()
所做的:阻止调用者以等待任务完成。如果没有它,并且在最后设置了较低的睡眠时间,则会调用runnable,但无法在主线程停止之前完成其工作
输出:世界代码>
这也是为什么在第一种情况下,helloworld
(首先是runnable的输出,然后是main的输出,这意味着主线程被阻塞,直到get()
返回)被写入,而第二个线程显示出阅读困难的微妙迹象:World Hello代码>
但这不是诵读困难症,它只是执行它被告知的事情。在第二种情况下,会发生以下情况:
1。可运行的被称为
2.主线程继续其进程,打印(“世界!)
3.睡眠时间设置为:runnable上1秒/main上5秒。(runnable的睡眠也可以在第二步中执行,但我将其放在这里是为了澄清行为)
4.可运行任务在1秒后打印(“Hello”),可完成的未来完成
5。5秒后,主线程停止
因此,您的runnable可以打印Hello
,因为它能够在这5秒超时之间执行命令
World! . . . . . .(1)Hello. . . . . . . . . . .(5)[END]
例如,如果将最后5秒的超时时间减少到0.5秒,则
World!. . (0.5)[END]
我不知道为什么case2的Runnable
块在工作
没有理由不起作用
runAsync(…)
方法表示以异步方式执行任务。假设应用程序没有过早结束,则无论您是否等待任务完成,任务最终都会完成
CompletableFuture
提供了多种等待任务完成的方法。但在您的示例中,您并没有将其用于此目的。相反,主方法中的线程.sleep(…)
调用具有相同的效果;也就是说,它等待任务(可能)完成的时间足够长。所以“您好“
在世界“
之前输出
重申一下,get()
调用不会导致任务发生。而是等待它发生
使用sleep
等待事件(如任务完成)发生是个坏主意:
睡眠不能判断事件是否发生过李>
你通常不知道事件发生需要多长时间,你也不知道要睡多久
如果你睡得太久,你就有“死时间”(见下文)
如果你没有睡足够长的时间,事件可能还没有发生。所以你需要一次又一次的测试和睡眠,然后
即使在本例中,理论上也有可能1在任务中的睡眠
之前完成main中的睡眠
基本上,CompletableFuture
的目的是提供一种有效的方法来等待任务完成并交付结果。你应该用它
举例说明。您的应用程序在输出“Hello”
和“World!”
之间等待(并浪费)约4秒。如果按预期使用CompletableFuture
,则不会有这4秒的“死区时间”
1-例如,某些外部代理可能能够选择性地“暂停”正在运行任务的线程。这可以通过设置断点来完成 CompletableFuture
的整个思想是,它们立即被安排启动(尽管您无法可靠地判断它们将在哪个线程中执行),当您到达get
或join
时,结果可能已经准备好,即CompletableFuture
可能已经完成。在内部,一旦管道中的某个阶段准备就绪,该特定的CompletableFuture
将设置为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();
这将输出:
started work
b
因此,即使工作开始了,最终的值也是b
,而不是a
,你为什么不希望它运行呢?@LouisWasserman我学到的材料中没有任何东西是我没有写下来的。所以我认为它不会起作用。就像流Api的终端操作一样。@LouisWasserman,像反应流这样的设计很常见,对于初学者来说,“推”和“拉”方法之间的区别并不总是显而易见的。好问题。“拉”行为的其他示例:在Python中,生成器实际上什么都不做
started work
b