So I have a couple futures which I want to run, even if some fail I'd like all to have a chance to run. So if I do:
CompletableFuture.allOf(futures).join()
Will that be the case? My reasoning is that every future would have its own queed job in its executor and therefore all would run provided the main thread doesn't finish first. My issue is that I specifically .join() on .allOf() so my application doesnt end before running everything
So allOf() semantics confuse me: Will the future return complete when all of the passed futures complete regardless if successful? Or will it complete a failed future if it sees one failed without waiting for the rest?
EDIT
To illustrate my question further, does .allOf behaves like this:
Stream.of(futures).forEach(future -> {
try {
future.join()
} catch (Throwable e) {
//dont throw, we want to join the rest
}
})
Or does it behaves like the following:
Stream.of(futures).forEach(future -> {
try {
future.join()
} catch (Throwable e) {
throw e; //All other remaining .join() wont run
}
})
Which is it? first or second case? Since I want the first case thats what I'm using on my code temporarily, but I'd like to use allOf() if possible because its more aesthetic
Thanks!
Yes, each future will be independently attempted for completion.
I think you are also trying to understand how the control flows in various scenarios. I have come up with 4 scenarios :
A future where failure shall happens due to an unhandled exception
A future which shall be explicitly marked as failed with a completeExceptionally AND has an exceptionally block at its tail.
A future which shall be explicitly marked as failed with a completeExceptionally AND does not have an exceptionally block at its tail.
A future that shall complete to success.
//CASE 1
// A future that shall fail due to an unandled exception in its run
// and has an exceptionally block at its tail
CompletableFuture<Void> unhandledFailureFutureWithExceptionHandler =
CompletableFuture.runAsync(() -> {
throw new RuntimeException("Exception in unhandledFailureFutureWithExceptionHandler");
});
unhandledFailureFutureWithExceptionHandler = unhandledFailureFutureWithExceptionHandler
.exceptionally(throwable -> {
// Handling exception for this future
// HANDLING POINT 1
System.out.println("Handling exception at HANDLING POINT FOR CASE 1,
failure message is : " + throwable.getMessage());
return null;
});
//CASE 2
//A future that shall fail and has an exceptionally block at its tail
CompletableFuture<Void> failedFutureWithExceptionHandler = new CompletableFuture<>();
failedFutureWithExceptionHandler.completeExceptionally(
new RuntimeException("Exception in failedFutureWithExceptionHandler")
);
failedFutureWithExceptionHandler = failedFutureWithExceptionHandler.exceptionally(throwable -> {
// Handling exception for this future
// HANDLING POINT 2
System.out.println("Handling exception at HANDLING POINT FOR CASE 2,
failure message is : " + throwable.getMessage());
return null;
});
//CASE 3
//A future that shall fail and has no exceptionally block at its tail
CompletableFuture<Void> failedFutureWithoutExceptionHandler = new CompletableFuture<>();
failedFutureWithoutExceptionHandler.completeExceptionally(
new RuntimeException("Exception in failedFutureWithoutExceptionHandler")
);
//CASE 4
//A future that shall succeed and print a message to console
CompletableFuture<Void> successFuture = CompletableFuture.runAsync(() ->
System.out.println("CASE 4 : Running successFuture")
);
CompletableFuture.allOf(unhandledFailureFutureWithExceptionHandler,
failedFutureWithExceptionHandler, failedFutureWithoutExceptionHandler, successFuture)
.exceptionally(throwable -> {
// Handling exception if ANY of the futures that did not have its own exceptionally block
// In this case the exception of `failedFutureWithoutExceptionHandler` will be handled here
// HANDLING POINT 3
System.out.println("Handling exception at HANDLING POINT FOR CASE 3,
failure message is : " + throwable.getMessage());
return null;
}).join();
The output produced on the console is
Handling exception at HANDLING POINT FOR CASE 1, failure message is : java.lang.RuntimeException: Exception in unhandledFailureFutureWithExceptionHandler
Handling exception at HANDLING POINT FOR CASE 2, failure message is : Exception in failedFutureWithExceptionHandler
CASE 4 : Running successFuture
Handling exception at HANDLING POINT FOR CASE 3, failure message is : java.lang.RuntimeException: Exception in failedFutureWithoutExceptionHandler
As you can see if a future throws an unhandled error as in case 1, if it has a exceptionally block chained to its tail, the exception shall be handled at that point
As for case 2, in case where the future is marked as failed with completeExceptionally, if the future has a handler chained to its tail, then the exceptionally block shall be handled by that block
In case 3, the future is marked as failed and does not have an exceptionally block, thus it shall be handled by the exceptionally block at the next level, in this case it is the exceptionally block of the allOf().
As you can see, case 4 runs to completion and the message gets print on the console irrespective of the failures of the other futures.
Related
I am making multiple async calls to my database. I store all those async calls on a List<CompletableFuture<X>> list. I want to collect all the results together, so I need to wait for all of those calls to complete.
One way is to create a CompletableFuture.allOf(list.toArray(...))...
Another way is to use: list.stream.map(cf -> cf.join())...
I was just wondering if there are any advantages of creating the global CompletableFuture and waiting for it to complete (when all the individual CompletableFuture complete) over directly waiting for the individual CompletableFutures to complete.
The main thread gets blocked either way.
static CompletableFuture<Void> getFailingCF() {
return CompletableFuture.runAsync(() -> {
System.out.println("getFailingCF :: Started getFailingCF.. ");
throw new RuntimeException("getFailingCF:: Failed");
});
}
static CompletableFuture<Void> getOkCF() {
return CompletableFuture.runAsync(() -> {
System.out.println("getOkCF :: Started getOkCF.. ");
LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(3));
System.out.println("getOkCF :: Completed getOkCF.. ");
});
}
public static void main(String[] args) {
List<CompletableFuture<Void>> futures = new ArrayList<>();
futures.add(getFailingCF());
futures.add(getOkCF());
// using CompletableFuture.allOf
var allOfCF = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
allOfCF.join();
// invoking join on individual CF
futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
In the code snippet above, the difference lies in handling exception: The CompletableFuture.allOf(..) wraps any exception thrown by any of the CompletableFutures while allowing rest of the threads (executing the CompletableFuture) continue their execution.
The list.stream.map(cf -> cf.join())... way immediately throws the exception and terminates the app (and all threads executing the CFs in the list).
Note that invoking join() on allOf throws the wrapped exception, too. It will also terminate the app. But, by this time, unlike list.stream.map(cf -> cf.join())..., the rest of the threads have completed their processing.
allOfCF.whenComplete(..) is one of the graceful ways to handle the execution result (normal or exceptional) of all the CFs:
allOfCF.whenComplete((v, ex) -> {
System.out.println("In whenComplete...");
System.out.println("----------- Exception Status ------------");
System.out.println(" 1: " + futures.get(0).isCompletedExceptionally());
System.out.println(" 2: " + futures.get(1).isCompletedExceptionally());
});
In the list.stream.map(cf -> cf.join())... way, one needs to wrap the join() call in try/catch.
Just going through the CompletableFuture documentation and stumbled upon the completeExceptionally and obtrudeException methods and is having a hard time comprehending the difference and use case. Can the community help understand the difference and the use case with an example?
Explanation
The difference is subtle but important. From the official documentation:
completeExceptionally​
If not already completed, causes invocations of get() and related methods to throw the given exception.
obtrudeException
Forcibly causes subsequent invocations of method get() and related methods to throw the given exception, whether or not already completed. [...]
So they differ in their behavior regarding CompletableFutures that are already completed.
Basically, a future can either be completed or still pending (not completed). When you call completeExceptionally or obtrudeException, the behavior differs depending on the state of the future at that point in time.
Already completed future
Consider this example where the future is already completed at the moment of calling the method:
CompletableFuture<String> future = CompletableFuture.completedFuture("hello world");
future.completeExceptionally(new RuntimeException("Oh noes!"));
System.out.println(future.get()); // Prints "hello world" just fine
versus
CompletableFuture<String> future = CompletableFuture.completedFuture("hello world");
future.obtrudeException(new RuntimeException("Oh noes!"));
System.out.println(future.get()); // Throws the exception
Not completed future
And in case the future is not completed yet, they will both throw an exception:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return "hello world";
});
future.completeExceptionally(new RuntimeException("Oh noes!"));
System.out.println(future.get());
and
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return "hello world";
});
future.obtrudeException(new RuntimeException("Oh noes!"));
System.out.println(future.get());
complete and obtrudeValue
Likewise there are also the methods complete and obtrudeValue which behave in the same way, but instead of throwing an exception, you can supply a value instead.
So complete basically completes the future with the given value, in case the future is not done yet, otherwise it does not do anything.
While obtrudeValue will supply the given value regardless, so it resets or cancels whatever the future already computed and replaces it by the given value instead.
completeExceptionally:
completableFuture.completeExceptionally(
new RuntimeException("Calculation failed!"));
//..
completableFuture.get(); //exception will be thrown whether `completableFuture` was not already completed.
obtrudeException:
completableFuture.obtrudeException(
new RuntimeException("Calculation failed!"));
//..
completableFuture.get(); //exception will be thrown **whether or not** `completableFuture` was completed.
I am trying to call cancel on CompletableFuture.
It seems from the docs:
If not already completed, completes this CompletableFuture with a CancellationException. Dependent CompletableFutures that have not already completed will also complete exceptionally, with a CompletionException caused by this CancellationException.
That it should complete them exceptionally which is what I was expecting but instead, it throws and immediate CancellationException.
Here is a sample code
CompletableFuture<?> f = CompletableFuture.supplyAsync(() -> false);
f.cancel(true); // Line 7.
f.join();
With a repro : https://www.mycompiler.io/view/2v1ME4u
Exception in thread "main" java.util.concurrent.CancellationException
at java.base/java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2396)
at Main.main(Main.java:7)
Line 7 is the f.cancel(true); line.
It doesn't actually throw immediately.
Calling f.cancel(true) causes a CancellationException to be created, capturing the stack trace of the call to cancel. So the stack trace (which is printed because it's unhandled) contains the line of the f.cancel(true); call.
But that exception isn't actually thrown until f.join():
Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally
...
Throws:
CancellationException - if the computation was cancelled
You can see this by putting in a few more print statements into your example code:
CompletableFuture<?> f = CompletableFuture.supplyAsync(() -> false);
f.cancel(true); // Line 8.
try {
f.join();
} catch (CancellationException e) {
System.out.println("CancellationException was thrown at call to f.join()");
e.printStackTrace(System.out);
}
Output:
CancellationException was thrown at call to f.join()
java.util.concurrent.CancellationException
at java.base/java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2396)
at Main.main(Main.java:8)
I am using Java 8, and I want to know the recommended way to enforce timeout on 3 async jobs that I would to execute async and retrieve the result from the future. Note that the timeout is the same for all 3 jobs. I also want to cancel the job if it goes beyond time limit.
I am thinking something like this:
// Submit jobs async
List<CompletableFuture<String>> futures = submitJobs(); // Uses CompletableFuture.supplyAsync
List<CompletableFuture<Void>> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
try {
allFutures.get(100L, TimeUnit.MILLISECONDS);
} catch (TimeoutException e){
for(CompletableFuture f : future) {
if(!f.isDone()) {
/*
From Java Doc:
#param mayInterruptIfRunning this value has no effect in this
* implementation because interrupts are not used to control
* processing.
*/
f.cancel(true);
}
}
}
List<String> output = new ArrayList<>();
for(CompeletableFuture fu : futures) {
if(!fu.isCancelled()) { // Is this needed?
output.add(fu.join());
}
}
return output;
Will something like this work? Is there a better way?
How to cancel the future properly? Java doc says, thread cannot be interrupted? So, if I were to cancel a future, and call join(), will I get the result immediately since the thread will not be interrupted?
Is it recommended to use join() or get() to get the result after waiting is over?
It is worth noting that calling cancel on CompletableFuture is effectively the same as calling completeExceptionally on the current stage. The cancellation will not impact prior stages. With that said:
In principle, something like this will work assuming upstream cancellation is not necessary (from a pseudocode perspective, the above has syntax errors).
CompletableFuture cancellation will not interrupt the current thread. Cancellation will cause all downstream stages to be triggered immediately with a CancellationException (will short circuit the execution flow).
'join' and 'get' are effectively the same in the case where the caller is willing to wait indefinitely. Join handles wrapping the checked Exceptions for you. If the caller wants to timeout, get will be needed.
Including a segment to illustrate the behavior on cancellation. Note how downstream processes will not be started, but upstream processes continue even after cancellation.
public static void main(String[] args) throws Exception
{
int maxSleepTime = 1000;
Random random = new Random();
AtomicInteger value = new AtomicInteger();
List<String> calculatedValues = new ArrayList<>();
Supplier<String> process = () -> { try { Thread.sleep(random.nextInt(maxSleepTime)); System.out.println("Stage 1 Running!"); } catch (InterruptedException e) { e.printStackTrace(); } return Integer.toString(value.getAndIncrement()); };
List<CompletableFuture<String>> stage1 = IntStream.range(0, 10).mapToObj(val -> CompletableFuture.supplyAsync(process)).collect(Collectors.toList());
List<CompletableFuture<String>> stage2 = stage1.stream().map(Test::appendNumber).collect(Collectors.toList());
List<CompletableFuture<String>> stage3 = stage2.stream().map(Test::printIfCancelled).collect(Collectors.toList());
CompletableFuture<Void> awaitAll = CompletableFuture.allOf(stage2.toArray(new CompletableFuture[0]));
try
{
/*Wait 1/2 the time, some should be complete. Some not complete -> TimeoutException*/
awaitAll.get(maxSleepTime / 2, TimeUnit.MILLISECONDS);
}
catch(TimeoutException ex)
{
for(CompletableFuture<String> toCancel : stage2)
{
boolean irrelevantValue = false;
if(!toCancel.isDone())
toCancel.cancel(irrelevantValue);
else
calculatedValues.add(toCancel.join());
}
}
System.out.println("All futures Cancelled! But some Stage 1's may still continue printing anyways.");
System.out.println("Values returned as of cancellation: " + calculatedValues);
Thread.sleep(maxSleepTime);
}
private static CompletableFuture<String> appendNumber(CompletableFuture<String> baseFuture)
{
return baseFuture.thenApply(val -> { System.out.println("Stage 2 Running"); return "#" + val; });
}
private static CompletableFuture<String> printIfCancelled(CompletableFuture<String> baseFuture)
{
return baseFuture.thenApply(val -> { System.out.println("Stage 3 Running!"); return val; }).exceptionally(ex -> { System.out.println("Stage 3 Cancelled!"); return ex.getMessage(); });
}
If it is necessary to cancel the upstream process (ex: cancel some network call), custom handling will be needed.
After calling cancel you cannot join the furure, since you get an exception.
One way to terminate the computation is to let it have a reference to the future and check it periodically: if it was cancelled abort the computation from inside.
This can be done if the computaion is a loop where at each iteration you can do the check.
Do you need it to be a CompletableFuture? Cause another way is to avoid to use a CompleatableFuture, and use a simple Future or a FutureTask instead: if you execute it with an Executor calling future.cancel(true) will terminate the computation if possbile.
Answerring to the question: "call join(), will I get the result immediately".
No you will not get it immediately, it will hang and wait to complete the computation: there is no way to force a computation that takes a long time to complete in a shorter time.
You can call future.complete(value) providing a value to be used as default result by other threads that have a reference to that future.
The javadoc for ExecutorService sometimes refers to the case when a Thread terminates 'due to failure'. However, it is not clear what kind of failure does this refer to.
For instance, the single thread executor documentation says that
if this single thread terminates due to a failure during execution
prior to shutdown, a new one will take its place if needed to execute
subsequent tasks
I would have thought that this situation might happen in case of an Exception, or maybe a RuntimeException, but it does not seem to be the case. Running the following code seems to be giving the same thread name and thread ID.
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
System.out.println("Hello from " + Thread.currentThread().getName()+ " " + Thread.currentThread().getId());
throw new NullPointerException("Test");
});
executor.submit(() -> {
System.out.println("Hello 2 from " + Thread.currentThread().getName() + " " + Thread.currentThread().getId());
});
The output of this code is:
Hello from pool-1-thread-1 12
Hello 2 from pool-1-thread-1 12
It seems that the same thread is being reused even in the case of NullPointerException.
So what kind of 'failure' is the Javadoc referring to?
This is an interesting question. Following the code in ThreadPoolExecutor the thread is discarded when a Runnable is passed to the execute() method.
When you call submit() the executor creates a wrapper for the callable/runnable of type FutureTask. FutureTask.run() has some logic to catch exceptions and store them (so then, you can query this from the Future). In this case, the exception never reaches the ThreadPool, so the thread is not discarded.
Augusto is right. Runnable tasks should have discarded the Thread after encountering the exception when they have passed as parameter in execute() method.
I have found concrete evidence regarding swallowing of exceptions by Future tasks at this article and Future Task source code
**Inside FutureTask$Sync**
void innerRun() {
if (!compareAndSetState(READY, RUNNING))
return;
runner = Thread.currentThread();
if (getState() == RUNNING) { // recheck after setting thread
V result;
try {
result = callable.call();
} catch (Throwable ex) {
setException(ex);
return;
}
set(result);
} else {
releaseShared(0); // cancel
}
}
protected void setException(Throwable t) {
sync.innerSetException(t);
}
There are few more interesting questions in SE around this topic.
Catching thread exceptions from Java ExecutorService
Choose between ExecutorService's submit and ExecutorService's execute
EDIT:
Thread failure or termination will happen when an exception is uncaught in the thread code. If you submit task by execute() instead of submit(), exception won't be caught unless you catch the exception. Uncaught exception by the thread code will result thread to terminate or failure and new thread will be created by Executor.
If you submit the task through submit(), a FutureTask will be created and that task will swallow uncaught exception by the code. Since the exception was caught in FutureTask, the thread won't be discarded.