JDK8 中的 CompletableFutures 什么时候阻塞执行线程?
When do CompletableFutures in JDK8 block the execution threads?
示例 1:
CometableFuture
.supplyAsync(new MySupplier())
.someCompletableFutureMethod(new SomeCompletableFutureComsumer())
ForkJoin 线程是否曾被阻塞?
示例 2:
final CompletableFuture cf = new CompletableFuture();
cf = executor.execute(new Runnable(){
public void run(){
//do work
cf.complete(result);
}
});
cf.whenComplete(new MyConsumer());
涉及的任何胎面是否被阻塞?
(我知道,我应该使用 Callable 而不是 Runnable :)
有什么方法可以在不使用从 Future 继承的方法的情况下滥用 API 来阻塞任何线程(main、ForkJoin、executor)?
假设我没有使用任何阻塞 APIs(我知道 future.get() 块)。
参见 javadoc get()
:
Waits if necessary for this future to complete, and then returns its result.
换句话说:当您在 CompletableFuture 上调用 "blocking" 方法时,它应该阻塞。否则不会。
该 javadoc 中的任何方法都没有这样的描述:可能会随机阻塞 ;-) !
CompletableFuture
添加了 join()
方法,这是 一种 Future.get()
的非检查异常版本(docs here).
不过我不建议使用它,因为如果不超时,它可能会挂起线程,而且您几乎总是可以使用 thenApply
和朋友重写代码。
我尝试通过始终使用 CompletionStage
来强制执行此操作,CompletableFuture
实现了这一点。
除了从 Future
继承的方法之外,我认为没有其他方法阻止。
所有这些方法都需要某种形式的同步,如果不阻塞就无法正确实现。
例如:
supplyAsync()
尝试通过其 execute()
方法在公共 ForkJoin
池中排队作业。此方法依赖于 Unsafe
和 awaitRunStateLock()
来排队作业;
- 类似的事情适用于您的
executor.execute()
,尽管您的实施可能不同;
- 关于
someCompletableFutureMethod()
(和 whenComplete()
):
- 除了
*Async()
方法,它首先需要检查这个future是否已经完成:如果是这样,传入的函数将在调用线程上执行,你可以考虑作为阻塞(尽管它实际上是在执行您的代码);
- 否则,任务必须排队,这依赖于循环和
Unsafe
来完成——参见 CompletableFuture.*push*(*)
方法。
cf.complete()
需要处理子任务,例如发送给 whenComplete()
的子任务:
- 它需要一些锁来确保这些任务只执行一次;
- 它将立即执行所有非异步任务(如 3.1 中);
- 它将需要安排异步任务(如 1. 和 2.)
- 大多数接受 lambda 作为参数的
CompletableFuture
方法(以及 return 一个新的 CompletableFuture
)实际上隐藏了一个 complete()
调用(在 return ed future) 将由执行 lambda 的同一个线程执行,因此像 4.¹ 中那样阻塞它
当然,除了在高度并发的环境中,许多线程试图同时推送和执行任务,你可能不会注意到这种阻塞。情况 3.1 和 4.2 是您最有可能遇到的情况,因为它们经常发生(如果您使用非 *Async()
方法)。
¹ thenCompose()
方法有一个微妙的例外,因为执行 complete()
调用的线程将取决于 CompletionStage
return 是否由 lambda 编辑是否已经完成。此外,非静态 *Async()
方法似乎重用了此调用的执行程序,因此可以使用另一个线程。
示例 1:
CometableFuture
.supplyAsync(new MySupplier())
.someCompletableFutureMethod(new SomeCompletableFutureComsumer())
ForkJoin 线程是否曾被阻塞?
示例 2:
final CompletableFuture cf = new CompletableFuture();
cf = executor.execute(new Runnable(){
public void run(){
//do work
cf.complete(result);
}
});
cf.whenComplete(new MyConsumer());
涉及的任何胎面是否被阻塞?
(我知道,我应该使用 Callable 而不是 Runnable :)
有什么方法可以在不使用从 Future 继承的方法的情况下滥用 API 来阻塞任何线程(main、ForkJoin、executor)?
假设我没有使用任何阻塞 APIs(我知道 future.get() 块)。
参见 javadoc get()
:
Waits if necessary for this future to complete, and then returns its result.
换句话说:当您在 CompletableFuture 上调用 "blocking" 方法时,它应该阻塞。否则不会。
该 javadoc 中的任何方法都没有这样的描述:可能会随机阻塞 ;-) !
CompletableFuture
添加了 join()
方法,这是 一种 Future.get()
的非检查异常版本(docs here).
不过我不建议使用它,因为如果不超时,它可能会挂起线程,而且您几乎总是可以使用 thenApply
和朋友重写代码。
我尝试通过始终使用 CompletionStage
来强制执行此操作,CompletableFuture
实现了这一点。
除了从 Future
继承的方法之外,我认为没有其他方法阻止。
所有这些方法都需要某种形式的同步,如果不阻塞就无法正确实现。
例如:
supplyAsync()
尝试通过其execute()
方法在公共ForkJoin
池中排队作业。此方法依赖于Unsafe
和awaitRunStateLock()
来排队作业;- 类似的事情适用于您的
executor.execute()
,尽管您的实施可能不同; - 关于
someCompletableFutureMethod()
(和whenComplete()
):- 除了
*Async()
方法,它首先需要检查这个future是否已经完成:如果是这样,传入的函数将在调用线程上执行,你可以考虑作为阻塞(尽管它实际上是在执行您的代码); - 否则,任务必须排队,这依赖于循环和
Unsafe
来完成——参见CompletableFuture.*push*(*)
方法。
- 除了
cf.complete()
需要处理子任务,例如发送给whenComplete()
的子任务:- 它需要一些锁来确保这些任务只执行一次;
- 它将立即执行所有非异步任务(如 3.1 中);
- 它将需要安排异步任务(如 1. 和 2.)
- 大多数接受 lambda 作为参数的
CompletableFuture
方法(以及 return 一个新的CompletableFuture
)实际上隐藏了一个complete()
调用(在 return ed future) 将由执行 lambda 的同一个线程执行,因此像 4.¹ 中那样阻塞它
当然,除了在高度并发的环境中,许多线程试图同时推送和执行任务,你可能不会注意到这种阻塞。情况 3.1 和 4.2 是您最有可能遇到的情况,因为它们经常发生(如果您使用非 *Async()
方法)。
¹ thenCompose()
方法有一个微妙的例外,因为执行 complete()
调用的线程将取决于 CompletionStage
return 是否由 lambda 编辑是否已经完成。此外,非静态 *Async()
方法似乎重用了此调用的执行程序,因此可以使用另一个线程。