CompletableFuture executes a task on a separate thread ( uses a thread-pool ) and provides a callback function. Let's say I have an API call in a CompletableFuture. Is that an API call blocking? Would the thread be blocked till it does not get a response from the API? ( I know main thread/tomcat thread will be non-blocking, but what about the thread on which CompletableFuture task is executing? )
Mono is completely non-blocking, as far as I know.
Please shed some light on this and correct me if I am wrong.
CompletableFuture is Async. But is it non-blocking?
One which is true about CompletableFuture is that it is truly async, it allows you to run your task asynchronously from the caller thread and the API such as thenXXX allows you to process the result when it becomes available. On the other hand, CompletableFuture is not always non-blocking. For example, when you run the following code, it will be executed asynchronously on the default ForkJoinPool:
CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
}
return 1;
});
It is clear that the Thread in ForkJoinPool that executes the task will be blocked eventually which means that we can't guarantee that the call will be non-blocking.
On the other hand, CompletableFuture exposes API which allows you to make it truly non-blocking.
For example, you can always do the following:
public CompletableFuture myNonBlockingHttpCall(Object someData) {
var uncompletedFuture = new CompletableFuture(); // creates uncompleted future
myAsyncHttpClient.execute(someData, (result, exception -> {
if(exception != null) {
uncompletedFuture.completeExceptionally(exception);
return;
}
uncompletedFuture.complete(result);
})
return uncompletedFuture;
}
As you can see, the API of CompletableFuture future provides you with the complete and completeExceptionally methods that complete your execution whenever it is needed without blocking any thread.
Mono vs CompletableFuture
In the previous section, we got an overview of CF behavior, but what is the central difference between CompletableFuture and Mono?
It worth to mention that we can do blocking Mono as well. No one prevents us from writing the following:
Mono.fromCallable(() -> {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
}
return 1;
})
Of course, once we subscribe to the future, the caller thread will be blocked. But we can always work around that by providing an additional subscribeOn operator. Nevertheless, the broader API of Mono is not the key feature.
In order to understand the main difference between CompletableFuture and Mono, lets back to previously mentioned myNonBlockingHttpCall method implementation.
public CompletableFuture myUpperLevelBusinessLogic() {
var future = myNonBlockingHttpCall();
// ... some code
if (something) {
// oh we don't really need anything, let's just throw an exception
var errorFuture = new CompletableFuture();
errorFuture.completeExceptionally(new RuntimeException());
return errorFuture;
}
return future;
}
In the case of CompletableFuture, once the method is called, it will eagerly execute HTTP call to another service/resource. Even though we will not really need the result of the execution after verifying some pre/post conditions, it starts the execution, and additional CPU/DB-Connections/What-Ever-Machine-Resources will be allocated for this work.
In contrast, the Mono type is lazy by definition:
public Mono myNonBlockingHttpCallWithMono(Object someData) {
return Mono.create(sink -> {
myAsyncHttpClient.execute(someData, (result, exception -> {
if(exception != null) {
sink.error(exception);
return;
}
sink.success(result);
})
});
}
public Mono myUpperLevelBusinessLogic() {
var mono = myNonBlockingHttpCallWithMono();
// ... some code
if (something) {
// oh we don't really need anything, let's just throw an exception
return Mono.error(new RuntimeException());
}
return mono;
}
In this case, nothing will happen until the final mono is subscribed. Thus, only when Mono returned by the myNonBlockingHttpCallWithMono method, will be subscribed, the logic provided to Mono.create(Consumer) will be executed.
And we can go even further. We can make our execution much lazier. As you might know, Mono extends Publisher from the Reactive Streams specification. The screaming feature of Reactive Streams is backpressure support. Thus, using the Mono API we can do execution only when the data is really needed, and our subscriber is ready to consume them:
Mono.create(sink -> {
AtomicBoolean once = new AtomicBoolean();
sink.onRequest(__ -> {
if(!once.get() && once.compareAndSet(false, true) {
myAsyncHttpClient.execute(someData, (result, exception -> {
if(exception != null) {
sink.error(exception);
return;
}
sink.success(result);
});
}
});
});
In this example, we execute data only when subscriber called Subscription#request so by doing that it declared its readiness to receive data.
Summary
CompletableFuture is async and can be non-blocking
CompletableFuture is eager. You can't postpone the execution. But you can cancel them (which is better than nothing)
Mono is async/non-blocking and can easily execute any call on different Thread by composing the main Mono with different operators.
Mono is truly lazy and allows postponing execution startup by the subscriber presence and its readiness to consume data.
Building up on Oleh's answer, a possible lazy solution for CompletableFuture would be
public CompletableFuture myNonBlockingHttpCall(CompletableFuture<ExecutorService> dispatch, Object someData) {
var uncompletedFuture = new CompletableFuture(); // creates uncompleted future
dispatch.thenAccept(x -> x.submit(() -> {
myAsyncHttpClient.execute(someData, (result, exception -> {
if(exception != null) {
uncompletedFuture.completeExceptionally(exception);
return;
}
uncompletedFuture.complete(result);
})
}));
return uncompletedFuture;
}
Then, later on you simply do
dispatch.complete(executor);
That would make CompletableFuture equivalent to Mono, but without backpressure, I guess.
Related
I just found the following code, it supplies an asynchronous task but immediately get the result (so if I understand correctly, it blocks the current thread until the result is available).
Is it efficient ?
public String myMethod() {
CompletableFuture<String> futur = CompletableFuture.supplyAsync(() -> {
// my long call to an external API
return "theResult";
});
try {
return future.get(FUTURE_TIMEOUT_DURATION, TimeUnit.MINUTES);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
If the timeout is handled correctly in the call to the external API, do I need this completable futur ?
Is it ok to simplify the code like this ?
public String myMethod() {
// my long call to an external API
return "theResult";
}
If you doesn't expect any problem with timeout you most probably can remove code related to feature.
There is possibility that code use some threadlocal variables or otherwise relies on being executed in separate thread.
The flow goes controller layer -> Service layer
Here I'm calling processLOBTransactions (Async method) method from the controller layer
How can I join all CompletableFuture responses in the controller layer? my requirement is after the execution of the processLOBTransactions for each list element from the controller, in the controller layer I wanna write logs kind of thing
Could anyone please give any suggestions on how to achieve this?
**Controller Layer:**
class ControllerLayer{
pktransaction.getLineOfBusinessTransaction().stream().forEach((lob) -> {
CompletableFuture<Boolean> futureResponse = flPbtService.processLOBTransactions(lob);
});
***//HERE How can i join all CompletableFuture Responses,and i wanna print logs like all thread completed***
}
**Service layer:**
class ServiceLayer{
#Async("threadPoolTaskExecutor")
public CompletableFuture<Boolean> processLOBTransactions(LineOfBusinessTransaction lobObj) {
// Doing some business logic and returning the CompletableFuture as Response
return CompletableFuture.completedFuture(new Boolean(true));
}
}
All the CompletableFuture<Boolean> futureResponse objects inside the forEach has to be stored in the Collection.
Assuming parallelStream() will not be used, ArrayList can be used to gather these references
Outside the loop, these references can be iterated and get() obtain the result of exception
get() might wait until the actual task completes, so it may be better to use get(timeout, Unit)` to have a deterministic SLA contracts
get can throw exception and be sure handle appropriate actions by catching the exception
if the get with timeout, could not complete within timeout, then you can request cancel if its not a high priority operation assuming the underlying task does not consume the interruption. (it a business logic)
ArrayList<CompletableFuture<Boolean>> futures = new ArrayList<>();
IntStream.range(0, 10).forEach((lob) -> {
CompletableFuture<Boolean> futureResponse = CompletableFuture.completedFuture(new Boolean(true));
futures.add(futureResponse);
});
for (CompletableFuture<Boolean> future : futures) {
try {
System.out.println(future.get());
// or future.get(1, TimeUnit.SECONDS)
} catch (InterruptedException | ExecutionException e) {
System.out.println(e.getMessage());
//future.cancel(true); // if need to cancel the underlying task, assuming the task listens
}
}
There is pretty heavy use of io.vertx.core.Future in the vertx ecosystem:
https://vertx.io/docs/apidocs/io/vertx/core/Future.html
An example of using Vertx Future is here:
private Future<Void> prepareDatabase() {
Future<Void> future = Future.future();
dbClient = JDBCClient.createShared(vertx, new JsonObject(...));
dbClient.getConnection(ar -> {
if (ar.failed()) {
LOGGER.error("Could not open a database connection", ar.cause());
future.fail(ar.cause()); // here
return;
}
SQLConnection connection = ar.result();
connection.execute(SQL_CREATE_PAGES_TABLE, create -> {
connection.close();
if (create.failed()) {
future.fail(create.cause()); // here
} else {
future.complete();
}
});
});
return future;
}
I was under the impression that io.vertx.core.Future had something to do with java.util.concurrent.Future, but it appears that it doesn't. As you can see the way to tell a Vertx future to fail is to call it's fail() method.
On the other hand, we have CompletableFuture which is an implementation of the java.util.concurrent.Future interface:
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
I don't see a fail method on the CompletableFuture, I only see "resolve()".
So my guess is that the only way to fail a CompletableFuture is to throw an Exception?
CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("fail this future");
return "This would be the success result";
});
besides throwing an error, is there a way to "fail" a CompletableFuture?
In other words, using a Vertx Future, we just call f.fail(), but what about with a CompletableFuture?
CompletableFuture encourages you to throw exceptions from supplyAsync() method to describe failures.
As mentioned in the comments, there's also completeExceptionally() method, which you can use in case you have a Future at hand, and would like to fail it.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#completeExceptionally-java.lang.Throwable-
Since Java9, there's also CompletableFuture.failedFuture​(Throwable ex) construct, if you want to return an already failed future.
https://docs.oracle.com/javase/9/docs/api/java/util/concurrent/CompletableFuture.html#failedFuture-java.lang.Throwable-
I'm playing around with implementing my own observables or porting them from other languages for fun and profit.
The problem I've run into is that there's very little info on how to properly test observables or async code in general.
Consider the following test code:
// Create a stream of values emitted every 100 milliseconds
// `interval` uses Timer internally
final Stream<Number> stream =
Streams.interval(100).map(number -> number.intValue() * 10);
ArrayList<Number> expected = new ArrayList<>();
expected.add(0);
expected.add(10);
expected.add(20);
IObserver<Number> observer = new IObserver<Number>() {
public void next(Number x) {
assertEquals(x, expected.get(0));
expected.remove(0);
if(expected.size() == 0) {
stream.unsubscribe(this);
}
}
public void error(Exception e) {}
public void complete() {}
};
stream.subscribe(observer);
As soon as the stream is subscribed to, it emits the first value. onNext is called... And then the test exits successfully.
In JavaScript most test frameworks nowadays provide an optional Promise to the test case that you can call asynchronously on success/failure. Is anything similar available for Java?
Since the execution is asyncronious, you have to wait until is finish. You can just wait for some time in an old fashion way
your_code
wait(1000)
check results.
Or if you use Observables you can use TestSubscriber
In this example you can see how having an async operation we wait until the observer consume all items.
#Test
public void testObservableAsync() throws InterruptedException {
Subscription subscription = Observable.from(numbers)
.doOnNext(increaseTotalItemsEmitted())
.subscribeOn(Schedulers.newThread())
.subscribe(number -> System.out.println("Items emitted:" + total));
System.out.println("I finish before the observable finish. Items emitted:" + total);
new TestSubscriber((Observer) subscription)
.awaitTerminalEvent(100, TimeUnit.MILLISECONDS);
}
You can see more Asynchronous examples here https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/scheduler/ObservableAsynchronous.java
I tried hard but didn't find any article or blog which clearly compares ListenableFuture and CompletableFuture, and provides a good analysis.
So if anyone can explain or point me to such a blog or article, it will be really good for me.
Both ListenableFuture and CompletableFuture have an advantage over its parent class Future by allowing the caller to "register" in one way or another a callback to be called when the async action has been completed.
With Future you can do this:
ExecutorService executor = ...;
Future f = executor.submit(...);
f.get();
f.get() gets blocked until the async action is completed.
With ListenableFuture you can register a callback like this:
ListenableFuture listenable = service.submit(...);
Futures.addCallback(listenable, new FutureCallback<Object>() {
#Override
public void onSuccess(Object o) {
//handle on success
}
#Override
public void onFailure(Throwable throwable) {
//handle on failure
}
})
With CompletableFuture you can also register a callback for when the
task is complete, but it is different from ListenableFuture in that it can be completed from any thread that wants it to complete.
CompletableFuture completableFuture = new CompletableFuture();
completableFuture.whenComplete(new BiConsumer() {
#Override
public void accept(Object o, Object o2) {
//handle complete
}
}); // complete the task
completableFuture.complete(new Object())
When a thread calls complete on the task, the value received from a call to get() is set with the parameter value if the task is not already completed.
Read about CompletableFuture
Guava AbstractFuture has its limitations:
Listener is lists, but usually only 1 used - overkill. If multiple listeners are needed, handle it inside the next stage, or think about messaging.
setException set return value as Exception, so user has to use instanceof to differentiate Exception or not at get() like guava AbstractFuture did.
In Future pipeline, too many layers addListener() make code hard to read.
I prefer CompletableFuture.supply().thenApply().thenAccept().handle()