CompletableFuture.allOf meaning - java

Do I understand it well, that when I use
CompletableFuture.allOf("array of CompletableFuture")
.runAsync(()-> { "piece of code" });
first I have to wait until the array of CF are all done , and than the Runnable "piece of code" is done?

The CompletableFuture.allOf static method allows to wait for completion of all of the Futures provided as a var-arg.
For example
CompletableFuture<String> future1
= CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> future2
= CompletableFuture.supplyAsync(() -> "Beautiful");
CompletableFuture<String> future3
= CompletableFuture.supplyAsync(() -> "World");
CompletableFuture<Void> combinedFuture
= CompletableFuture.allOf(future1, future2, future3);

That is explicit in the documentation:
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complet

Related

CompletableFuture to execute multiple DB queries asynchronously

I want to execute multiple DB queries parallelly and store the results in a map. I am trying to do it like this but the map is not getting populated completely when I am accessing the map.
Am I doing anything wrong?
public Map<MapKeyEnums, Set<String>> doDBCalls(String phoneNumber, long timestamp) {
Map<MapKeyEnums, Set<String>> instrumentsEdgesMap = new EnumMap<>(MapKeyEnums.class);
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "ABC", timestamp)).
thenApply(x -> instrumentsEdgesMap.put(MapKeyEnums.ABC, x));
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "XYZ", timestamp)).
thenApply(x -> instrumentsEdgesMap.put(MapKeyEnums.XYZ, x));
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "DEF", timestamp)).
thenApply(x -> instrumentsEdgesMap.put(MapKeyEnums.DEF, x));
return instrumentsEdgesMap;
}
Any help will be much appreciated, thanks in advance.
In the above approach supplyAsync will be executed by the Async thread from ForkJoinPool, but thenApply method is always executed by calling thread. So your queries will run one after the another in sequence which it is not Asynchronous
All async methods without an explicit Executor argument are performed using the ForkJoinPool.commonPool() (unless it does not support a parallelism level of at least two, in which case, a new Thread is created to run each task).
Here is the example
CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName());
return "SupplyAsync";
}).thenAccept(i->{
System.out.println(Thread.currentThread().getName()+"--"+i);
});
Output :
ForkJoinPool.commonPool-worker-3
main--SupplyAsync
So if you want your process to be Async then first trigger all three db queries with supplyAsync and capture the output within CompletableFuture
CompletableFuture<Set<String>> first = CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "ABC", timestamp));
CompletableFuture<Set<String>> second = CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "XYZ", timestamp));
CompletableFuture<Set<String>> third = CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "DEF", timestamp));
And then now create a stream with three of them and then collect them to Map
Stream.of(new AbstractMap.SimpleEntry<MapKeyEnums, CompletableFuture<Set<String>>>(MapKeyEnums.ABC, first),
new AbstractMap.SimpleEntry<MapKeyEnums, CompletableFuture<Set<String>>>(MapKeyEnums.XYZ, second),
new AbstractMap.SimpleEntry<MapKeyEnums, CompletableFuture<Set<String>>>(MapKeyEnums.DEF, third))
.forEach(entry->{
entry.getValue().thenAccept(val-> instrumentsEdgesMap.put(entry.getKey(), val));
});
You have to wait for the futures to complete before you return the result.
Try something like
public Map<MapKeyEnums, Set<String>> doDBCalls(String phoneNumber, long timestamp) {
Map<MapKeyEnums, Set<String>> instrumentsEdgesMap = new EnumMap<>(MapKeyEnums.class);
CompletableFuture.allOf(
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "ABC", timestamp))
.thenAccept(x -> instrumentsEdgesMap.put(MapKeyEnums.ABC, x)),
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "XYZ", timestamp))
.thenAccept(x -> instrumentsEdgesMap.put(MapKeyEnums.XYZ, x)),
CompletableFuture.supplyAsync(() -> dbReadService.getCall(phoneNumber, PhoneNumber.class, "DEF", timestamp))
.thenAccept(x -> instrumentsEdgesMap.put(MapKeyEnums.DEF, x)))
.get(); // wait for completion of all three subtasks
return instrumentsEdgesMap;
}

Keeping track of success of Completable Futures

Is there a way to track the status of failures in Completeable Futures?
I have a scenario where I have three futures where the put() can either succeed or throw a RunTime Exception; and I only want two to succeed. In other words, if the first and second put succeeds, I want to cancel the third and don't want that future to complete. However, if the first one fails, I want to go ahead with the other two.
How do I keep track of exceptions in a CompletableFuture and how can I cancel one future in a set of Futures based on number of successes?
final CompletableFuture<Try<Void>> a = CompletableFuture.supplyAsync(() -> put());
final CompletableFuture<Try<Void>> b = CompletableFuture.supplyAsync(() -> put());
final CompletableFuture<Try<Void>> c = CompletableFuture.supplyAsync(() -> put());
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(a,b,c);
combinedFuture.get();
I think a Phaser will be useful here:
Phaser phaser = new Phaser(2);
final CompletableFuture<Void> a =
CompletableFuture.supplyAsync(() -> { put(); phaser.arrive(); return null; });
final CompletableFuture<Void> b =
CompletableFuture.supplyAsync(() -> { put(); phaser.arrive(); return null; });
final CompletableFuture<Void> c =
CompletableFuture.supplyAsync(() -> { put(); phaser.arrive(); return null; });
CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(a,b,c);
phaser.awaitAdvanceInterruptibly(0);
new Phaser(2) means the Phaser will wait for two arrive() calls before marking itself “terminated,” which will cause any methods awaiting advancement to return.

Difference between Java8 thenCompose and thenComposeAsync

Given this piece of code:
public List<String> findPrices(String product){
List<CompletableFuture<String>> priceFutures =
shops.stream()
.map(shop -> CompletableFuture.supplyAsync(
() -> shop.getPrice(product), executor))
.map(future -> future.thenApply(Quote::parse))
.map(future -> future.thenCompose(quote ->
CompletableFuture.supplyAsync(
() -> Discount.applyDiscount(quote), executor
)))
.collect(toList());
return priceFutures.stream()
.map(CompletableFuture::join)
.collect(toList());
}
This part of it:
.map(future -> future.thenCompose(quote ->
CompletableFuture.supplyAsync(
() -> Discount.applyDiscount(quote), executor
)))
Could it be rewrite as:
.map(future ->
future.thenComposeAsync(quote -> Discount.applyDiscount(quote), executor))
I took this code from an example of a book and says the two solutions are different, but I do not understand why.
Let's consider a function that looks like this:
public CompletableFuture<String> requestData(String parameters) {
Request request = generateRequest(parameters);
return CompletableFuture.supplyAsync(() -> sendRequest(request));
}
The difference will be with respect to which thread generateRequest() gets called on.
thenCompose will call generateRequest() on the same thread as the upstream task (or the calling thread if the upstream task has already completed).
thenComposeAsync will call generateRequest() on the provided executor if provided, or on the default ForkJoinPool otherwise.

Java Multi-Thread Executor InvokeAll Problems

The code I'm having problems with is:
Executor executor = (Executor) callList;
List<ProgState> newProgList = executor.invokeAll(callList).stream()
.map(future -> {try {return future.get();} catch(Exception e){e.printStackTrace();}})
.filter(p -> p!=null).collect(Collectors.toList());
The method invokeAll(List>) is undefined for the type Executor
I am told I should use an executor like the one in the code snippet.
The Callables are defined within the following code:
List<Callable<ProgState>> callList = (List<Callable<ProgState>>) lst.stream()
.map(p -> ((Callable<ProgState>)(() -> {return p.oneStep();})))
.collect(Collectors.toList());
Here is the teacher's code:
//prepare the list of callables
List<Callable<PrgState>> callList = prgList.stream().map(p -> (() -> {return p.oneStep();})).collect(Collectors.toList());
//start the execution of the callables
//it returns the list of new created threads
List<PrgState> newPrgList = executor.invokeAll(callList).stream()
.map(future -> { try {
return future.get();
}
catch(Exception e) {
//here you can treat the possible
// exceptions thrown by statements
// execution
}
})
.filter(p -> p!=null).collect(Collectors.toList());
//add the new created threads to the list of existing threads
prgList.addAll(newPrgList);
If you can use stream(), why not parallelStream() as it would be much simpler.
List<PrgState> prgStates = prgList.parallelStream()
.map(p -> p.oneStep())
.collect(Collectors.toList());
This way you have no thread pool to configure, start or stop when finished.
Some might suggest that parallelStream() was the main reason for adding Stream and lambdas to Java 8 in the first place. ;)
You can't cast list of Callables with ExecutorService. You need to define ExecutorService which will inturn pick up callables and execute them in one or multiple threads in parallel.
This is what i think you are after:
ExecutorService executor = Executors.newCachedThreadPool();//change executor type as per your need.
List<ProgState> newProgList = executor.invokeAll(callList).stream().map(future -> {...

How to add returned values from a Supplier to a FutureList

i want to run the three methods posted below using CompletableFuture asynchronous supplier so that, when the Executor finishes the Futurelist should contain three values returned from the three methods respectively.
i know how to use the Futurelist, for an example:
futureList = CompletableFuture.supplyAsync()
but in my case, i want something like:
futureList.add(CompletableFuture.supplyAsync())
please let me know how can i do that.
methods:
this.compStabilityMeasure(this.frameIjList, this.frameIkList, SysConsts.STABILITY_MEASURE_TOKEN);
this.setTrackingRepValue(this.compTrackingRep(this.frameIjList, this.frameIkList, SysConsts.TRACKING_REPEATABILITY_TOKEN));
this.setViewPntRepValue(this.compViewPntRep(this.frameIjList, this.frameIkList, SysConsts.VIEWPOINT_REPEATABILITY_TOKEN));
compStabilityMeasure method implementation:
private void compStabilityMeasure(ArrayList<Mat> frameIjList, ArrayList<Mat>
frameIkList, String token) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
synchronized (frameIjList) {
synchronized (frameIjList) {
this.setRepValue(this.compRep(frameIjList, frameIkList, token));
this.setSymRepValue(this.compSymRep(this.getRepValue(), frameIkList, frameIjList, token));
}
}
}
You want to look at using "thenCombineAsync", eg:
CompletableFuture<String> firstFuture = firstMethod();
CompletableFuture<String> secondFuture = secondMethod();
CompletableFuture<String> thirdFuture = thirdMethod();
CompletableFuture<List<String>> allCompleted = firstFuture
.thenCombineAsync(secondFuture, (first, second) -> listOf(first, second))
.thenCombineAsync(thirdFuture, (list, third) -> {
list.add(third);
return list;
});
You can use allOf, and then create a CompletableFuture that gets completed with a Stream containing the results of your individual CompletableFutures:
CompletableFuture<String> cf1 = CompletableFuture.supplyAsync(() -> "hi1");
CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> "hi2");
List<CompletableFuture<String>> cfsList = Arrays.asList(cf1, cf2);
CompletableFuture<Void> allCfs = CompletableFuture.allOf((CompletableFuture<String>[]) cfsList.toArray());
CompletableFuture<Stream<String>> cfWithFinishedStream = allCfs.thenApply((allCf) ->
cfsList.stream().map(cf -> cf.getNow("")));
Example to get the values from the stream when the CF completes:
cfWithFinishedStream.thenAccept(stream ->
stream.forEach(string -> System.out.println(string)));
If you don't like streams, you can convert them to a List using collect:
CompletableFuture<List<String>> cfWithFinishedList = allCfs
.thenApply((allCf) ->
cfsList.stream().map(cf ->
cf.getNow("")).collect(Collectors.toList()));

Categories

Resources