If I have
CompletableFuture<Something> future1 = service.request(param1);
CompletableFuture<Something> future2 = service.request(param2);
CompletableFuture<Void> many = CompletableFuture.allOf(future1, future2);
what will happen when I do many.cancel()? Will future1 and future2 be cancelled as well? If not, what would be the cleanest way to achieve this? I'm reluctant to hold on to future1 and future2, just to be able to cancel them when I want to cancel many.
Some background on why I want this: when receiving a piece of data, I need to request matching, potentially future data to perform a computation. If a newer piece of data arrives, I want to cancel the completion of the earlier computation, because the result will immediately be superceded by the new computation.
Before you make you life harder than necessary, you should become aware of what cancelling a CompletableFuture actually does. Most important, it does not stop the associated computation.
If a computation associated with a CompletableFuture is already running, but has not completed yet, cancelling a CompletableFuture turns it into the “cancelled” state, which may have an immediate effect on all dependent stages, but not on the computation, which will continue until complete, though its attempt to complete the cancelled future will not have any effect.
While other Future’s might be cancelled with interruption, which will stop the computation, if it checks for interruption, this doesn’t apply to CompletableFuture, see CompletableFuture.cancel(boolean):
Parameters:
mayInterruptIfRunning - this value has no effect in this implementation because interrupts are not used to control processing.
So when you cancel either, future1 or future2, successfully, the only immediate effect would be the cancellation of many, which you can also achieve by calling cancel on many itself. It would have a broader effect, if there were more dependent stages, but since you stated, that you don’t want to keep references to future1 or future2, this doesn’t seem to be the case.
The following code demonstrates the behavior:
CompletableFuture<String> supply = CompletableFuture.supplyAsync(() -> {
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2));
System.out.println("supplying value");
return "foo";
});
CompletableFuture<String> then = supply.thenApply(s -> {
System.out.println("Evaluating next stage");
return s;
});
CompletableFuture<?> last = then.handle((s,t) -> {
System.out.println("last stage: value: "+s+", throwable: "+t);
return "";
});
System.out.println("cancelling: "+supply.cancel(true));
ForkJoinPool.commonPool().awaitQuiescence(1, TimeUnit.DAYS);
This code reproducible prints:
last stage: value: null, throwable: java.util.concurrent.CompletionException: java.util.concurrent.CancellationException
canceling: true
supplying value
(the order might change)
regardless of whether you call supply.cancel(true) or then.cancel(true) or whether you pass true or false; it won’t stop the ongoing Supplier evaluation.
There will be a difference, if the associated computation hasn’t been started yet and it does check the cancellation state when starting, like with the actions produced by the convenience methods in CompletableFuture. This is a rare situation, as normally, your service.request(paramN) call is supposed to trigger the evaluation.
It’s a fundamental property of the CompletableFuture, as its name suggests, that it is completable, i.e. anyone could call complete on it, thus, the CompletableFuture can’t control whoever might eventually call complete on it in the future. So all, cancel can achieve, is to set it to the cancelled state, which implies ignoring subsequent completion attempts and propagating the cancellation downward to the dependent actions.
So the bottom line is that you might already be fine with just calling cancel on the many instance, because calling cancel on future1 and future2 is unlikely to have an effect that is worth the complication of your code.
The tree constructed by CompletableFuture.allOf doesn't hold any references to the given instances of CompletableFuture. Instead if just builds completion tree, which is is completed when all of the given CompletableFutures complete (from JavaDocs).
So probably you have to keep references to all CompletableFuture in order to cancel them sequentially when it is needed.
You can give a try for my library: https://github.com/vsilaev/tascalate-concurrent
It provides both truly cancelable CompletionStage implementations (CompletableTask) as well as methods to combine them (Promises.all)
CompletionStage<Something> future1 = CompletableTask
.complete(param1, myExecutor).thenApplyAsync(this::serviceCall);
CompletionStage<Something> future2 = CompletableTask
.complete(param2, myExecutor).thenApplyAsync(this::serviceCall);
Promise<List<Something>> many = Promises.all(future1, future2);
Now you can call many.cancel(true) and both future1 and future2 will be cancelled (if not yet completed). Moreover, if either of individual futures completes exceptionally, then another one will be cancelled automatically (again, if not yet completed).
Related
I'm studying CompletableFuture API and there is an example:
CompletableFuture.completedFuture(url)
.thenComposeAsync(this::readPage, executor)
.thenApply(this::getImageURLs)
.thenApply(this::saveFoundImages)
.....
I have a question: if I call the thenComposeAsync(...) method as the first one, will the other methods in the chain execute in the executor which I passed through the params, or I should call the other methods using async to get asynchronous execution in a particular executor?
OK, so there are 3 types of methods in CompletableFuture. For example:
thenApply()
thenApplyAsync(Function) (without an Executor)
thenApplyAsync(Function, Executor) (with an Executor)
The last one means that this action will execute in the Executor that you pass to it and it is the most obvious one.
The second one means that the action is executed in the ForkJoinPool.
The first one is far more interesting. The documentation makes it sound like it's easy, via:
Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method
And you need to start bisecting this into smaller pieces. What you need to understand that there are threads that complete a certain CompletableFuture, there are threads that execute some actions on it and there are threads that chain certain dependent actions. Potentially, these are all different threads. And this is where it all begins:
If the dependent action is already chained, the thread that will call complete is going to be the thread that executes this action.
If the future is already completed, then the thread that chains the action will execute it.
Since there is no linear actions on the steps above, it is literally impossible to say for sure in which thread your thenApply will execute, at least with 100% certainty. That action can be executed in any of :
the thread that calls complete/completeExceptionally
the thread that does the chaining of thenApply
the thread that calls join/get
Any of the above is a possibility. If you really want I made a rather interesting test here, proving some of the things above.
I am not trying to pick on the other answer, but he made a rather interesting point that I was very confused about in the begging too:
In your example: After .thenComposeAsync also all the following chained futures will get completed by executor.
We can easily prove that this is not correct:
CompletableFuture<String> future1 = CompletableFuture.completedFuture("a");
CompletableFuture<String> future2 = future1.thenApplyAsync(x -> "b" + x, Executors.newCachedThreadPool());
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
CompletableFuture<String> future3 = future2.thenApply(x -> {
System.out.println(Thread.currentThread().getName());
return x + "c";
});
future3.join();
What you are going to see if you run this, is that main actually executes thenApply, not a thread from the pool.
I have a ListenableFuture that I am creating like:
ListenableFuture<X> future1 = ...
ListenableFuture<X> future2 = ...
Futures.addCallback(future1, futureCallback, executor);
Now inside the futureCallback I do :
public void onSuccess(Object result) {
Object f2 = future2.get();
.
.
. <do something with f2>
}
Is it a bad thing to do a .get() call in a callback? I was told that I could get into a hung up state. Is that true and why would it so happen?
What is the alternative of achieving the same thing in a better way?
You probably want something more like:
whenAllSucceed(future1, future2).run(...)
The Runnable you pass to run will still have to call future2.get. Still, whenAllComplete has some advantages over the addCallback approach:
The addCallback approach may require a thread to block on future2.get until future2 is done. (This is at least a small waste of resources. If you run out of threads, your code may hang. Or you might try to create a new thread and run out of memory.) With whenAllComplete, your Runnable won't run until both input futures are done, so future2.get will not block.
whenAllComplete returns a Future. You can use this to check for errors. And if you cancel it, it will cancel both input futures.
Note also that whenAllSucceed will log if multiple inputs fail. This isn't always desirable. If you don't want the logging, you might prefer whenAllComplete. (Hopefully someday we will provide a way to turn off the logging.)
Doing a .get() call in a callback is outright usual -- it's what you want to do most of the time -- but a more standard pattern is to, instead, use transform or transformAsync to pass in a function that takes the result directly.
Doing blocking operation in callback (e.g. future.get) destroys the idea of asynchronous programming, turning asynchronous program in ordinary multithreaded. So this is indeed an antipattern.
It's OK, however, to call get() if you are sure it would not block.
If you want a callback which waits for 2 asynchronous results, you can use CompletableFuture instead:
CompletableFuture<X> future1 = ...
CompletableFuture<X> future2 = ...
CompletableFuture future3 = future1.thenCombineAsync(future 2, futureCallback, executor);
I have a method on some repository class that returns a CompletableFuture. The code that completes these futures uses a third party library which blocks. I intend to have a separate bounded Executor which this repository class will use to make these blocking calls.
Here is an example:
public class PersonRepository {
private Executor executor = ...
public CompletableFuture<Void> create(Person person) {...}
public CompletableFuture<Boolean> delete(Person person) {...}
}
The rest of my application will compose these futures and do some other things with the results. When these other functions that are supplied to thenAccept, thenCompose, whenComplete, etc, I don't want them to run on the Executor for the repository.
Another example:
public CompletableFuture<?> replacePerson(Person person) {
final PersonRepository repo = getPersonRepository();
return repo.delete(person)
.thenAccept(existed -> {
if (existed) System.out.println("Person deleted"));
else System.out.println("Person did not exist"));
})
.thenCompose(unused -> {
System.out.println("Creating person");
return repo.create(person);
})
.whenComplete((unused, ex) -> {
if (ex != null) System.out.println("Creating person");
repo.close();
});
}
The JavaDoc states:
Actions supplied for dependent completions of non-async methods may be performed by the thread that completes the current CompletableFuture, or by any other caller of a completion method.
Side question: Why is there an or here? In what case is there another caller of a completion method that does not complete the current future?
Main question: If I want all the println to be executed by a different Executor than the one used by the repository, which methods do I need to make async and provide the executor manually?
Obviously the thenAccept needs to be changed to thenAcceptAsync but I'm not sure about that point onwards.
Alternative question: Which thread completes the returned future from thenCompose?
My guess is that it will be whatever thread completes the future returned from the function argument. In other words I would need to also change whenComplete to whenCompleteAsync.
Perhaps I am over complicating things but this feels like it could get quite tricky. I need to pay a lot of attention to where all these futures come from. Also from a design point of view, if I return a future, how do I prevent callers from using my executor? It feels like it breaks encapsulation. I know that all the transformation functions in Scala take an implicit ExecutionContext which seems to solve all these problems.
Side Question: If you assigned the intermediate CompletionStage to a variable and call a method on it, it would get executed on the same thread.
Main Question: Only the first one, so change thenAccept to thenAcceptAsync -- all the following ones will execute their steps on the thread that is used for the accept.
Alternative Question: the thread that completed the future from thenCompose is the same one as was used for the compose.
You should think of the CompletionStages as steps, that are executed in rapid succession on the same thread (by just applying the functions in order), unless you specifically want the step to be executed on a different thread, using async. All next steps are done on that new thread then.
In your current setup the steps would be executed like this:
Thread-1: delete, accept, compose, complete
With the first accept async, it becomes:
Thread-1: delete
Thread-2: accept, compose, complete
As for your last question, about the same thread being used by your callers if they add additional steps -- I don't think there is much you can do about aside from not returning a CompletableFuture, but a normal Future.
Just from my empirical observations while playing around with it, the thread that executes these non-async methods will depend on which happens first, thenCompose itself or the task behind the Future.
If thenCompose completes first (which, in your case, is almost certain), then the method will run on the same thread that is executing the Future task.
If the task behind your Future completes first, then the method will run immediately on the calling thread (i.e. no executor at all).
Is a Future task considered finished, isDone()==true when it hits the return statement?
For example:
ExecutorService executor = newSingleThreadExecutor();
Future<> result = executor.submit(new Runnable()
{
#Override
public void run()
{
return;
}
}
Is result considered done when it hits the return ?
More or less. The javadoc is ambiguous for what it exactly means but generally, when the callable returns, very shortly after isDone() will be true (or if an exception occurs the same is true).
Completion of Future.get is considered to pin point a Future's completion.
To quote from java docs
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task...
In other words till the time Future doesn't complete the call to Future.get will be blocked and hence completion of Future.get should be considered as completion of Future (normal or exceptional)
P.S.: Future.isDone should only be used to check the status and no way an indication as when the Future was completed.
Long story short: I have a collection of Future objects. Some of them are already in progress, some are not. I iterate the collection and call future.cancel(false) which, according to the documentation, should cancel all Futures that are not currently running but should allow all the others to complete.
My question is: How do I know when a particular Future is completed after I have called future.cancel(false)? future.isDone() always returns true because cancel() was indeed called before that and future.get() always throws a CancellationException even though the Future is still running.
Any suggestions?
Since Future models the future result of a pending computation, and since that result is not forthcoming from a canceled future, it is reasonable that Future gives you no way to find out when the computation whose result has been disposed of will complete. In other words, you'd need another paradigm to achieve your goal with that approach.
If your wish is to wait for all the submitted tasks to complete, the closest thing which is directly supported by the Executor Service API is to shut down the entire executor service and wait for its termination.
If the above does not fit your solution, then I don't see a better approach than some custom solution, for example a custom implementation of Runnable, which does some housekeeping on the side so you can check when it has completed running.
You could add a flag to your Future implementation which will reflect the actual Future' state
given a Callable<Object> c:
futureTask1 = new FutureTask<Object>(c);
futureTask2 = new FutureTask<Void>(futureTask1, null);
executor.execute(futureTask2);
now if you want the result:
futureTask1.get()
if you're no longer interested in the result:
futureTask1.cancel(mayInterruptIfRunning)
if you want to wait to be sure the code in the callable is not (and will not become) running (whether never called, finished cancelling or finished producing the result):
futureTask2.get()
Even if cancelled before it started working, this waits for the executor to execute the scheduled task (which will to nothing if already cancelled), so this may unnecessariliy wait for other long-running tasks to complete. YMMV