will CompletableFuture callback always be executed - java

I have two service calls:
String call1() { ... return "ok"; }
void call2(String) { ... }
I know the basic way for CompletableFuture with callback is like
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
What if I separate the two chained CompletableFutures, like:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
Is this any different from calling join() on future2:
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
What if I call join() on both of the futures?
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
It seems they are all the same from running my sample code. But I feel something might be wrong somewhere. Thanks!

They are not the same.
In short, you can look at it as future1 and future2 hold results of distinct tasks (even if future2 uses the result of future1, it's a different future).
future1.join() will block until () -> call1() ends, and future2's task won't start until then. future2.join() will wait until s -> call2(s) is done.
What if I separate the two chained CompletableFutures, like:
This makes no difference as far as task execution is concerned. It's either a question of style or it only matters when you need to use the two future objects separately.
What if I call join() on both of the futures?
It's redundant in this case to call future1.join() as you are not doing anything between the two .join calls. It would make sense if you wanted to perform some action "after completion of task1 and before the completion of task 2".
In this case, though, calling future2.join() is enough.
And the code snippet below should show how this behaves:
public static void main(String[] args) {
CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> delay());
CompletableFuture<Void> future2 = future1.thenRun(() -> delay());
long start = System.currentTimeMillis();
future1.join();
System.out.println("Future 1 done. Waiting for future 2: "
+ (System.currentTimeMillis() - start));
future2.join();
System.out.println("Future 2 complete: "
+ (System.currentTimeMillis() - start));
}
static void delay() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
As it is, this code outputs:
Future 1 done. Waiting for future 2: 5001
Future 2 complete: 10001
But when you remove future1.join(), the output becomes:
Future 2 complete: 10001
Which simply means that future1.join() is superfluous unless you have actions to perform between the completions of the two futures

The supplyAsync and thenAccept methods should be executed on a separate thread automatically according to the documentation:
All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool()
In your examples, the only difference is your thread waits on different events before continuing due to the joining. Here is the breakdown:
CompletableFuture<Void> future = CompletableFuture
.supplyAsync(() -> call1())
.thenAccept(s -> call2(s));
future.join();
This will wait until call2 completes because that is the future returned by thenAccept method.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join(); // will call2 be executed at this time?
This will only wait until call1 completes and moves on. call2 will still gets executed.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future2.join();
This is identical to the first one.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> call1());
CompletableFuture<Void> future2 = future1.thenAccept(s -> call2(s));
future1.join();
future2.join();
Calling future1.join() ends when call1 completed, then future2.join() ends when call2 completed. This should be identical functionally with the first one as well.

Related

Why CompletableFuture did not execute in sequence?

My goal is to understand how CompletableFuture works.
My expected result: If I do CompletableFuture.runAsync().thenRun().thenRunAsync(). The thread will be executed in sequence runAsync() -> thenRun() -> thenRunAsync().
My actual result: The sequence is race condition. Sometimes:
runAsync -> thenRunAsync+e -> ...
runAsync -> thenRun -> ...
Reference from SO
public class RunExample5 {
public static void main(String[] args) {
ExecutorService e = Executors.newSingleThreadExecutor(r -> new Thread(r, "sole thread"));
CompletableFuture<?> f = CompletableFuture.runAsync(() -> {
System.out.println("runAsync:\t" + Thread.currentThread());
LockSupport.parkNanos((int) 1e9);
}, e);
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()));
f.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()),
e);
LockSupport.parkNanos((int) 2e9);
e.shutdown();
}
}
You need
f.thenRun(() -> System.out.println("thenRun:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync:\t" + Thread.currentThread()))
.thenRunAsync(() -> System.out.println("thenRunAsync+e:\t" + Thread.currentThread()), e);
The interface to CompletableFuture doesn't work quite the way you're imagining. f itself doesn't keep track of every call to thenRun or thenRunAsync and run them in order; instead, it treats everything from thenRun or thenRunAsync as simultaneously runnable as soon as the main work completes. If you want to chain more complicated sequences of work, you need to use the return value of thenRun or thenRunAsync -- a CompletionStage object -- and call thenRunAsync on that.

Java CompletableFutures: is it ever a acceptable to not join/get?

Is it ever correct to not return or join on a CompletableFuture?
That is, is this code ever correct?
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
In the above code, we never know if the Futures complete successfully, or at all. (And I don't even know that the Futures created in noJoin are not eligible for garbage collection; but presumably the ForkJoinPool.commonPool() holds onto those references?)
In any case, if it truly doesn't matter to our program whether or not those Futures succeed, a better implementation of noJoin is just a no-op:
void noJoin() {}
If on the other hand, it does matter, we need to either join on or return both Futures, by composing them (either serially with thenCompose, or in parallel with thenCombine or allOf) and then join the composed Future or return it to our caller.
By joining, we block and throw an exception if either Future is exceptional; or better, by returning a Future, we remain asynchronous while ensuring that our caller gets a Future that holds any exceptional result:
Future<Void> returnBothAsync() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
return CompletableFuture.allOf(cfa, cfb);
}
or
void joinBothBlocking() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect);
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
CompletableFuture.allOf(cfa, cfb).get(50L, TimeUnit.MILLISECONDS);
}
I think this is true even if we arrange to handle all exceptions:
void noJoin() {
CompletableFuture<Void> cfa = CompletableFuture
.supplyAsync(() -> "a")
.thenAccept(this::someSideEffect)
.exceptionally(e -> {
Logger.log(e);
return DEFAULT;
});
CompletableFuture<Void> cfb = CompletableFuture
.supplyAsync(() -> "b")
.thenAccept(this::someSideEffect);
}
because even if the exceptional are handled / "can't happen", we still don't know if the Future ever completed at all.
Or am I wrong, and there are cases where code like that in noJoin is correct?
This is not a complete answer to your question. It probably depends on the exact use case to be able to tell how to handle CompletableFuture and their results.
If you choose to not wait for the results of the CompletableFutures, you will likely have to make sure that the used executor finishes all tasks. In your case the used executor is ForkJoinPool.commonPool() whose documentation says:
[...] However this pool and any ongoing processing are automatically terminated upon program System.exit(int). Any program that relies on asynchronous task processing to complete before program termination should invoke commonPool().awaitQuiescence, before exit.

CompletableFuture return all finished result after timeout

I want to have a list of comletablefutures I want to wait. With following code.
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures) {
CompletableFuture<Void> allDoneFuture =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return allDoneFuture.thenApply(
v -> futures.stream().filter(Objects::nonNull)
.map(future -> future.join())
.collect(Collectors.toList())
);
}
CompletableFuture<List<Result>> allResponse = finishAllQuery(futures);
allResponse.get(5, milliseconds)
The problem is that among all the futures, some of them can be slow, I want that after the expiration time, the get method return with all completed results. Is there a way to do that?
Thanks a lot.
This should be handled by finishAllQuery itself. E.g., starting with Java 9, you can use
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures, long timeOut, TimeUnit unit) {
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.completeOnTimeout(null, timeOut, unit)
.thenApply(v -> futures.stream()
.filter(CompletableFuture::isDone)
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
With completeOnTimeout, we can force the completion of the future with a predefined value after the timeout elapsed. Here, we just use null, as the result value of allOf doesn’t matter anyway.
We just have to add a filter condition, to skip all futures which are not completed yet, as otherwise, join would block the thread.
This can be used like
CompletableFuture<List<Result>> allResponse
= finishAllQuery(futures, 5, TimeUnit.MILLISECONDS);
List<Result> list = allResponse.join(); // will wait at most 5 milliseconds
For Java 8, we can use
static <T> CompletableFuture<T> completeOnTimeout(
CompletableFuture<T> cf, T value, long timeOut, TimeUnit unit) {
ScheduledExecutorService e = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<Boolean> job = e.schedule(() -> cf.complete(value), timeOut, unit);
return cf.whenComplete((x,y) -> { job.cancel(false); e.shutdown(); });
}
for the missing feature, which requires a small rewrite:
public static <T> CompletableFuture<List<T>> finishAllQuery(
List<CompletableFuture<T>> futures, long timeOut, TimeUnit unit) {
return completeOnTimeout(
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])),
null, timeOut, unit)
.thenApply(v -> futures.stream()
.filter(CompletableFuture::isDone)
.map(CompletableFuture::join)
.collect(Collectors.toList()));
}
The way, the caller uses the method, doesn’t change.
For production use, it’s worth rewriting the completeOnTimeout method to reuse the ScheduledExecutorService, but this also requires adding shutdown code or a thread factory creating daemon threads. With Java 9 or newer, you get that for free.
If you don't necessarily want all futures to complete, then you should not use allOf(). You can instead use CompletionService and then use the poll() method with timeout. So the flow will be:
Wrap your ExcecutorService in a CompletionService
Submit N tasks to CompletionService. Then poll for individual tasks with timeout. Something like:
elapsedTime = 0;
availableTime = 5ms;
completedFutures = List();
while (elapsedTime < availableTime):
remainingTime = availableTime - elapsedTime;
startTime = currentTime();
completedFutures.add(CompletionService.poll(timeout=remainingTime));
elapsedTime += (currentTime() - startTime)

CompletableFuture.thenAccept can indeed block

Unlike stated in some blogs(e.g. I can't emphasize this enough: thenAccept()/thenRun() methods do not block) CompletableFuture.thenAccept can indeed block. Consider the following code, uncommenting the pause method call will cause thenAccept to block:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
log.trace("return 42");
return "42";
});
//pause(1000); //uncommenting this will cause blocking of thenAccept
future.thenAccept((dbl -> {
log.trace("blocking");
pause(500);
log.debug("Result: " + dbl);
}));
log.trace("end");
pause(1000);
Can we be sure that the following will not block? It's my understanding that if the supplyAsync runs immediately then the thenAccept could block, no?
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
return "42";
}).thenAccept((dbl -> {
pause(500);
log.debug("Result: " + dbl);
}));
You are right, thenAccept() will block if the future is already completed. Also note that when it is not the case, it will cause the thread that completes it to block at the time of completion.
This is why you have thenAcceptAsync(), which will run your Consumer in a non-blocking way:
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
return "42";
}).thenAcceptAsync((dbl -> {
pause(500);
log.debug("Result: " + dbl);
}));
See also Which executor is used when composing Java CompletableFutures?

CompletableFuture -- Aggregate Future to Fail Fast

I have been using the CompletableFuture.allOf(...) helper to create aggregate futures that will only become "done" when their composite futures are marked as complete, i.e:
CompletableFuture<?> future1 = new CompletableFuture<>();
CompletableFuture<?> future2 = new CompletableFuture<>();
CompletableFuture<?> future3 = new CompletableFuture<>();
CompletableFuture<?> future = CompletableFuture.allOf(future1, future2, future3);
I would like a slight variation on this functionality, where the aggregate future is market as complete when:
All futures have completed successfully OR
Any one future has completed unsuccessfuly
In the latter case, the aggregate future should complete (exceptionally) immediately, and not have to wait for the other futures to complete, i.e. to fail-fast.
To illustrate this in contrast to CompletableFuture.allOf(...) consider this:
// First future completed, gotta wait for the rest of them...
future1.complete(null);
System.out.println("Future1 Complete, aggregate status: " + future.isDone());
// Second feature was erroneous! I'd like the aggregate to now be completed with failure
future2.completeExceptionally(new Exception());
System.out.println("Future2 Complete, aggregate status: " + future.isDone());
// Finally complete the third future, that will mark the aggregate as done
future3.complete(null);
System.out.println("Future3 Complete, aggregate status: " + future.isDone());
Using allOf(...), this code yields:
Future1 Complete, aggregate status: false
Future2 Complete, aggregate status: false
Future3 Complete, aggregate status: true
Whereas my alternative aggregate implementation would return "true" after Feature2 was completed, given it was an exceptional.
I cannot find any utils in the Java standard library that will help me achieve this, which feels strange... given it's a relatively vanilla use-case.
Looking at the implementation of CompletableFuture.allOf(...) it's fairly obvious that the logic behind these scenarios is fairly complex. I'd loathe to have to write this myself, I was wondering if there are any alternatives?
Although not as syntactically sweet as the CompletableFuture.allOf(...) method, it appears that thenCompose(...) may offer the solution:
CompletableFuture<?> future = future1.thenCompose((f) -> future2).thenCompose((f) -> future3);
This will yield the desired:
Future1 Complete, aggregate status: false
Future2 Complete, aggregate status: true
Future3 Complete, aggregate status: true
This could be wrapped up in a helper method which would offer some syntactic niceties to the caller:
private static CompletableFuture<?> composed(CompletableFuture<?> ... futures) {
// Complete when ALL the underlying futures are completed
CompletableFuture<?> allComplete = CompletableFuture.allOf(futures);
// Complete when ANY of the underlying futures are exceptional
CompletableFuture<?> anyException = new CompletableFuture<>();
for (CompletableFuture<?> completableFuture : futures) {
completableFuture.exceptionally((t) -> {
anyException.completeExceptionally(t);
return null;
});
}
// Complete when either of the above are satisfied
return CompletableFuture.anyOf(allComplete, anyException);
}
Allowing for:
CompletableFuture<?> future = composed(future1, future2, future3);
You could maybe do this by creating both an allOf and an anyOf, and then combining those into a second anyOf.

Categories

Resources