Reputation: 1856
Example 1:
CompletableFuture
.supplyAsync(new MySupplier())
.someCompletableFutureMethod(new SomeCompletableFutureComsumer())
Does the ForkJoin thread ever get blocked?
Example 2:
final CompletableFuture cf = new CompletableFuture();
cf = executor.execute(new Runnable() {
public void run() {
//do work
cf.complete(result);
}
});
cf.whenComplete(new MyConsumer());
Do any of the treads involved ever get blocked?
( I know, I should use Callable instead of Runnable :)
Is there any way I can misuse the API without using the method inherited from Future to block any thread (main, ForkJoin, executor)?
Assuming I am not using any blocking APIs (I know future.get() blocks).
Upvotes: 1
Views: 6308
Reputation: 20608
All those methods will need some form of synchronization, which cannot be implemented properly without ever blocking.
For instance:
supplyAsync()
tries to queue a job in the common ForkJoin
pool, through its execute()
method. This method relies on an Unsafe
and awaitRunStateLock()
to queue the job;executor.execute()
, though your implementation might be different;someCompletableFutureMethod()
(and whenComplete()
):
*Async()
methods, it will first need to check whether this future is already completed: if that's the case, the passed-in function will be executed on the calling thread, which you might consider as blocking (though it is actually executing your code);Unsafe
to do this – see the CompletableFuture.*push*(*)
methods.cf.complete()
will need to take care of the child tasks, such as the one sent to whenComplete()
:
CompletableFuture
methods that accept a lambda as parameter (and return a new CompletableFuture
) actually hide a complete()
call (on the returned future) that will be executed by the same thread that executed the lambda, hence blocking it like in 4.¹Of course, except in a highly concurrent environment where many threads are trying to push and execute tasks at the same time, you will probably not notice this blocking. Cases 3.1 and 4.2 are the ones you are the most likely to encounter as they occur quite often (if you use the non-*Async()
methods).
¹ There is a subtle exception with the thenCompose()
method as the thread that executes the complete()
call will depend on whether the CompletionStage
returned by the lambda is already completed or not. Additionally, the non-static *Async()
methods seem to reuse the executor for this call, so another thread could be used.
Upvotes: 3
Reputation: 140613
See the javadoc for get()
:
Waits if necessary for this future to complete, and then returns its result.
In other words: when you call "blocking" methods on a CompletableFuture, it should block. Otherwise, it will not.
No method in that javadoc has a description that reads: might randomly block ;-) !
Upvotes: 4
Reputation: 2298
CompletableFuture
adds the join()
method, which is kind of a non-checked exception version of Future.get()
(docs here).
I don't recommend using it though because, by not taking a timeout, it can hang the thread and you can almost always rewrite the code using thenApply
and friends.
I try to enforce this by always using CompletionStage
, which CompletableFuture
implements.
I don't think any other methods except the ones inherited from Future
block.
Upvotes: 4