Get result of completablefuture - java

I am using completablefuture to be returned from an async thread in springboot application. My implementation is below. From my understanding a new thread should be started for each item in list and should process parallely. I understand .get will block the execution but as it is parallely running still i dont see any improvement in performance. Any suggestions on below please to improve the performance?
ServiceA.java
#Autowired
ServiceB serviceb;
public List<String> getNames(List<Item> items) {
List<CompletableFuture<String>> list = new ArrayList<>();
List<String> returnList = new ArrayList<>();
for( Item item: items) {
CompletableFuture<String> getItemName = serviceb.doProcess(item);
list.add(getItemName):
}
for( CompletableFuture name : list) {
returnList.add(name.get());
}
return returnList;
}
ServiceB.java
Class ServiceB {
#Async
Public CompletableFuture<String> doProcess(Item item)
{
//do process
}

You could call allOf to wait for all results. This will wait for all CompletableFuture to complete.
List<String> returnList = new ArrayList<>(items.size());
CompletableFuture<String>[] tasks = items.stream()
.map(value-> serviceb.doProcess(value).thenApply(returnList::add))
.toArray(CompletableFuture[]::new);
// wait for all tasks to finish
CompletableFuture.allOf(tasks).get(50, TimeUnit.SECONDS);
// return the results
return returnList;
Second solution would be to use an reactive approach like publisher/subscriber pattern (Spring WebFlux or JavaRx). This way your application would have little/no waiting operation. But this would affect your application architecture.
One Advice:
In order to create an CompletableFuture use the constructor with ExecutorService in order to keep in check number of threads and have control over running threads or when application shuts down.

You can use thenAccept to add the items to the list.
List<String> list = new CopyOnWriteArrayList<>();
CompletableFuture.allOf(
Stream.of(items).map(
i -> CompletableFuture.supplyAsync(i)
.thenAccept(list::add)
).toArray(CompletableFuture[]::new)
).get(10, SECONDS);
return list;

The get() will definitely not going to work out as it will block the main thread of execution to fetch the result from the async threads. A better option would be to use callback methods(thenApply() etc) accordingly so as to let the main thread continue its work.

Related

Best way to use Future in java

I have a set of jobs which I am submitting using executor framework and Future. Let's say that I have 100 futures. As of now, I am using Future.get and using the output for subsequent processing. However for further tuning, I want to change the flow as below:
iterate through the set of future tasks and start consuming the result as soon as a future task is complete. I am reading the API doc to understand what might be a good way to accomplish this but reaching out to see if there is a better way to accomplish what I am looking for.
Here is the sample code:
public class ImplCallable implements Callable<String> {
int timeOut;
ImplCallable(int timeOut) {
this.timeOut=timeOut;
}
public String call() throws Exception {
Thread.sleep(timeOut);
return Thread.currentThread().getName();
}
}
and the main class:
public class MainProg {
public static void main(String...args) throws Exception {
long startTimeInMillis = System.currentTimeMillis();
ImplCallable callable1 = new ImplCallable(1000);
ImplCallable callable2 = new ImplCallable(2000);
ExecutorService service = Executors.newFixedThreadPool(4);
Future<String> task1 = service.submit(callable1);
Future<String> task2 = service.submit(callable2);
List<Future<String>> futureList = new ArrayList();
futureList.add(task1);
futureList.add(task2);
String retVal;
for(Future<String> task:futureList) {
retVal = task.get();
//do something with the retVal
}
long endTimeInMillis = System.currentTimeMillis();
System.out.println("time taken by code - " + (endTimeInMillis-startTimeInMillis) + "-ms");
}
}
Basically I don't want to use Future.get() and wait for its completion. I want to know if either of the task is complete and use the result as soon as its done.
There are many ways do this so, without a concrete example, you won't get a concrete answer. Likely want to look at CompletableFuture which has many methods for defining follow-on work, combining work, splitting work etc.
Future<String> f = CompletableFuture.supplyAsync(() -> "INITIAL WORK")
.thenApply(String::toLowerCase) // Do some more work
.thenAccept(queue::add); // put results onto a queue something is reading from
f.join();
// Batch complete
I hope you are using Java 8 or later version.
Whenever you mention "as soon as a future task is complete", you want to use CompletableFuture and its .thenApply() method, as #drekbour suggests.
Then you have multiple threads running different tasks in non-determenistic sequence. But at the end you want to get all the results in the single (Main) thread. To achieve it, you can use CompletableFuture.allOf method, .join() it - and then iterate over all the (already completed) future results without waiting.

Do I need to control the execution of these two threads with the main threads?

void process(String question){
Callable<ResponseList> callable1 = () -> this.stsCompute question);
Future<ResponseList>task1 = executorService.submit(callable1);
Callable<ResponseList> callable2 = () -> this.dssmCompute(question);
Future<ResponseList>task2 = executorService.submit(callable2);
try{
ResponseList stsResponse = task1.get();
ResponseList dssmResponse = task2.get();
}catch (Exception e){
e.printStackTrace();
}
// Do I need to wait until the first 2 threads complete?
processResponse(stsResponse, dssmResponse);
}
In this "process" method, I have two additional threads 'callable1' & 'callable2' to concurrently execute. I want to make sure only when these two tasks complete, the method in the main thread 'processResponse()' can start to be executed.
In such a case, do I need to add any additional control to ensure the order of the execution, is it already good enough? If not, how to make that control happen?
You should use ExecutorService.invokeAll which will return List of Futures when complete. Besides I would use a shorter syntax, something like
List<Future> futures = executorService.invokeAll(Arrays.asList(()->dssmCompute(), ()->dssmCompute()));
With Java8+ i would suggest use Completable Futures. It supports exactly the use case you are trying to achieve.
Completable Futures: Java 8
Sample Algorithm looks like:
var cf = CompletableFuture.supplyAsync(() -> processQuestion()).runAsync(() -> processResponse)
Note: var is typeInference supports in java 10+
Also, there are plenty of Examples on Completable Futures

ThreadPoolExecutor, Futures: correlating requests and responses

I'm trying to use a ThreadPoolExecutor along with Future (results) and Callable (task to be executed), but I can't figure out a simple way of correlating input (Callable) with the corresponding result (Future), it seems the only sensible way would be to create a wrapper (example) that contains all items, but this might be too much overhead for such a simple task.
Am I wrong? Any suggested alternatives?
A better approach would be to use the invokeAll() method instead of submit(). You need to provide a collection of Callables to it and it will return a collection of Futures in the same sequential order as your tasks. Moreover, invokeAll() lets you define a timeout, so you don't need latches. It will be something like that:
List<Callable> jobs = new ArrayList<>(requests.size());
for (String request : requests) {
jobs.add(new MyCallable(request));
}
List<Future<ProcessedResponse>> futures = executor.invokeAll(jobs, timeout, TimeUnit.MILLISECONDS);
Iterator<String> it = requests.iterator();
for (Future<ProcessedResponse> future: futures) {
String request = it.next(); // This request corresponds to this future
if (future.isDone()) {
results.add(new Result(request, future.get()));
} else {
future.cancel(true);
}
}

How to check if all tasks running on ExecutorService are completed

I'v got ConcurrentLinkedDeque which I'm using for synchronic push/pop elements,
and I'v got some async tasks which are taking one element from stack and if this element has neighbors It's pushing it to stack.
Example code:
private ConcurrentLinkedDeque<Item> stack = new ConcurrentLinkedDeque<>();
private ExecutorService exec = Executors.newFixedThreadPool(5);
while ((item = stack.pollFirst()) != null) {
if (item == null) {
} else {
Runnable worker = new Solider(this, item);
exec.execute(worker);
}
}
class Solider{
public void run(){
if(item.hasNeighbors){
for(Item item:item.neighbors){
stack.push(item)
}
}
}
}
I would like to have additional statement in while loop which answers the question - "any task in Executor is working?"
There isn't a clean way to check if all Runnables are done if you use ExecutorService.execute(Runnable). Unless you build a mechanism to do so in the Runnable itself (which is sloppy in my opinion).
Instead:
Use ExecutorService.submit(Runnable). This method will return a Future<?> which is a handle to the result of a Runnable. Using Futures provides a clean way to check results.
All you have to do is maintain a list of Futures that you submit, and then you can iterate over the whole list of Futures and either:
A) wait for all the futures to be done in a blocking way or
B) check if all the futures are done in a non-blocking way.
Here is a code example:
List<Future<?>> futures = new ArrayList<Future<?>>();
ExecutorService exec = Executors.newFixedThreadPool(5);
// Instead of using exec.execute() use exec.submit()
// because it returns a monitorable future
while((item = stack.pollFirst()) != null){
Runnable worker = new Solider(this, item);
Future<?> f = exec.submit(worker);
futures.add(f);
}
// A) Await all runnables to be done (blocking)
for(Future<?> future : futures)
future.get(); // get will block until the future is done
// B) Check if all runnables are done (non-blocking)
boolean allDone = true;
for(Future<?> future : futures){
allDone &= future.isDone(); // check if future is done
}
Update: with Java 8+ CompletableFutures you can manage this with its new callback functions. First you will need to create all of the CompletableFutures you need which will also start running, eg:
We need to accumulate all the futures generated in an Array in order to pass them later to CompletableFuture.allOf(CompletableFutures...)
So let's say you have a list of people you want to calculate its days until birthday asynchronously:
First we create all those needed futures and collect them together in an array:
CompletableFuture<?>[] completables = people.stream()
.map(p -> createCompletableFuture(p))
.toArray(CompletableFuture<?>[]::new);
private CompletableFuture createCompletableFuture(Person p) {
return CompletableFuture.runAsync(daysUntillBirthday(p));
}
Then you pass those completables to a new CompletableFuture:
CompletableFuture c = CompletableFuture.allOf(completables)
And you can now check if there are still futures running with:
c.isDone()
This may not be the cleanest solution, but you can use ThreadPoolExecutor.getActiveCount() to check how many threads are actively executing tasks.
Implementing this within a while loop with a simple condition to check if the active thread count is zero is a palatable solution.
Here is a code example:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
for (int x = 0; x < 4; x++) {
Runnable worker = new Solider(this,item);
executor.execute(worker);
}
// Now check for active threads.
while(executor.getActiveCount()!=0)
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
executor.shutdown();
The while block directly answers your question.
IE - If the while block is active, tasks are being executed.

Control Multithreading in Java

I have one "Runnable" threads which is initiating few "Callable" threads and I want to display results when all above threads has finished their jobs.
What is the best way to do it?
My code is as follows
Connector.java (Starting Runnable Thread)
public class Connector {
private static void anyFileConnector() {
// Starting searching Thread
ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(traverse, executor);
//HERE I WANT MY ALL SEARCH RESULTS/OUTPUT : CURRENTLY IT IS STARTING OTHER THREADS AND NOT SHOWING ME ANY RESULTS BECAUSE NONE OF THEM WAS FINISHED.(IN CONSOLE, I WAS ABLE TO SEE RESULTS FROM ALL THE THREADS
setSearchResult(traverse.getResult());
executor.shutdown();
}
}
Traverse.java (Runnable Thread)
I am using ExecutorCompletionService to handle it...but it didn't create any difference.
:(
public class Traverse implements Runnable {
public void run() {
ExecutorService executor = Executors.newFixedThreadPool(100);
ExecutorCompletionService<List<ResultBean>> taskCompletionService =
new ExecutorCompletionService<List<ResultBean>>(executor);
try (DirectoryStream<Path> stream = Files
.newDirectoryStream(dir)) {
Search newSearch = new Search();
taskCompletionService.submit(newSearch);
}
list.addAll(taskCompletionService.take().get());
}
}
Search.java (Callable Thread)
public class Search implements Callable<List<ResultBean>> {
public List<ResultBean> call() {
synchronized (Search.class) {
// It will return results
return this.search();
}
}
}
Go for CyclicBarrier and you will be able to achieve this.
A cyclic barrier will perform a task as soon as all the threads are done with their work, this is where you can print the en result.
Check this lik for working of CyclicBarrier : http://javarevisited.blogspot.com/2012/07/cyclicbarrier-example-java-5-concurrency-tutorial.html
Easy - all the Callables will return Future objects which you can used to wait and get the result by calling Future.get() in a blocking wait way. So your problem is just a for loop waiting for each future on the callables blockingly.
After that, just aggregate the results to return to client.
The submit method of executor service can return a list of Future objects. What you can do for your case is call isDone() method of these Future objects in a while loop.
Whenever, any future task gets completed this method will return true. You can now call get() method on this to get the value returned by this task. In this way you could get hold of all the future task values without having to wait for any particular task to get complete (since your first future task could have the longest completion time)

Categories

Resources