San*_*Lee 15 java multithreading completable-future
我在学习时有一个问题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
Run Code Online (Sandbox Code Playgroud)
输出:
Hello
World!
Run Code Online (Sandbox Code Playgroud)
此代码既不调用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
Run Code Online (Sandbox Code Playgroud)
输出:
World!
Hello
Run Code Online (Sandbox Code Playgroud)
我不知道为什么案例 2 的可运行块正在工作。
Ste*_*n C 12
我不知道为什么
Runnable
case2 块在工作。
没有理由为什么它不起作用。
该runAsync(...)
方法表示异步执行任务。假设应用程序不会过早结束,无论您是否等待它完成,任务最终都会完成。
该CompletableFuture
规定的等待任务完全不同的方式。但是在您的示例中,您并没有为此目的使用它。相反,Thread.sleep(...)
您的 main 方法中的调用具有相同的效果;即它等待的时间足够长,任务已经(可能)完成。"Hello"
之前的输出也是如此"World"
。
重申一下,get()
调用不会导致任务发生。而是等待它发生。
使用sleep
等待事件(例如任务完成)发生是一个坏主意:
即使在这个例子中,在理论上是可行1的sleep
主前完成sleep
的任务。
基本上, 的目的CompletableFuture
是提供一种有效的方式来等待任务完成并交付结果。你应该用它...
为了显示。您的应用程序在输出"Hello"
和"World!"
. 如果您CompletableFuture
按预期使用 ,则不会有那 4 秒的“死区时间”。
1 - 例如,某些外部代理可能能够有选择地“暂停”正在运行任务的线程。可以通过设置断点来完成...
的整个想法CompletableFuture
是立即安排它们启动(尽管您无法可靠地判断它们将在哪个线程中执行),并且当您到达get
or 时join
,结果可能已经准备好了,即:CompletableFuture
可能已经是完成。在内部,一旦管道中的某个阶段准备就绪,该特定阶段CompletableFuture
将被设置为完成。例如:
String result =
CompletableFuture.supplyAsync(() -> "ab")
.thenApply(String::toUpperCase)
.thenApply(x -> x.substring(1))
.join();
Run Code Online (Sandbox Code Playgroud)
是一样的:
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();
Run Code Online (Sandbox Code Playgroud)
到您实际调用 时join
,cf3
可能已经完成。get
并且join
只是阻塞直到所有阶段都完成,它不会触发计算;立即安排计算。
一个小补充是,您可以在CompletableFuture
不等待管道执行完成的情况下完成 a :像complete
, completeExceptionally
, obtrudeValue
(即使它已经完成,这个设置它),obtrudeException
或cancel
。这是一个有趣的例子:
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());
}
Run Code Online (Sandbox Code Playgroud)
这将输出:
started work
b
Run Code Online (Sandbox Code Playgroud)
所以即使工作开始了,最终的值是b
,而不是a
。
第二种情况是“工作”,因为你让主线程睡了足够长的时间(5 秒)。工作在引号之间,因为它不是真的工作,只是完成。我在这里假设代码应该输出Hello World!
,以便被认为“正常工作”。
在这两种情况下,在主线程结束时尝试使用此睡眠时间的相同代码:
Thread.sleep(100);
1 . 第一个会以相同的方式运行,因为 get 操作会阻塞主线程。事实上,对于第一种情况,你甚至不需要最后的睡眠时间。
输出: Hello World!
2 . 第二种情况不会输出Hello
,因为没有人告诉主线程:“嘿,等待这个完成”。这就是get()
:阻止调用者以等待任务完成。没有它,并在最后设置一个低睡眠时间,可运行被调用,但在主线程停止之前无法完成其工作。
输出: World!
这也是为什么在第一种情况下Hello World!
(首先是 runnable 的输出,然后是 main 的输出 - 意味着主线程被阻塞直到get()
返回)被写入,而第二个显示出阅读障碍的微妙迹象的原因:World Hello!
但它不是阅读障碍,它只是执行它被告知的。在第二种情况下,会发生这种情况:
1. runnable 被调用。
2.主线程继续其进程,打印 ("World!)
3. Sleep
时间设置:可运行1秒/主5秒。(runnable 的 sleep 也可以在第二步执行,但我把它放在这里是为了澄清行为)
4. runnable 任务在 1 秒后打印 ("Hello"),CompletableFuture 完成。
5. 5 秒过去了,主线程停止。
所以你的 runnable 可以打印,Hello
因为它能够在这 5 秒超时之间执行命令。
World! . . . . . .(1)Hello. . . . . . . . . . .(5)[END]
Run Code Online (Sandbox Code Playgroud)
例如,如果将最后 5 秒的超时时间减少到 0.5 秒,则会得到
World!. . (0.5)[END]
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2183 次 |
最近记录: |