0

I have an async method as shown below which calls my Task class and my Task class does all the work.

    @Override
    public Future<DataResponse> executeAsync(DataKey key) {
        Future<DataResponse> future = null;

        try {
            Task task = new Task(key, restTemplate);
            future = executor.submit(task); 
        } catch (Exception ex) {
            // logging exception here
        }

        return future;
    }

Below is my Task class which does all the work:

public class Task implements Callable<DataResponse> {

    private DataKey key;
    private RestTemplate restTemplate;

    public Task(DataKey key, RestTemplate restTemplate) {
        this.key = key;
        this.restTemplate = restTemplate;
    }

    @Override
    public DataResponse call() throws Exception {
        // some code here
    }
}

Now I need to call executeAsync method in parallel and then make a List<DataResponse> object and return it.

@Override
public List<DataResponse> executeSync(DataKey key) {
    List<DataResponse> responseList = new ArrayList<DataResponse>();

    // make a List of DataKey using single key passed to this method.       
    List<DataKey> keys = new ArrayList<DataKey>();

    for(DataKey key : keys) {


    }
}

How can I call executeAsync method in parallel and return back responseList? In my keys list maximum I will have six DataKey object.

john
  • 11,311
  • 40
  • 131
  • 251

3 Answers3

1

If you're expecting to return a List<DataResponse> containing the DataResponse objects returned by Task#call, you can't do this asynchronously. You'll need to block inside executeSync to wait for the result of all futures.

List<Future> futures = new ArrayList<>(keys.size());
for(DataKey key : keys) {
    Future<DataResponse> future = executeAsync(key);
    futures.add(future);
}

for (Future<DataResponse> future : futures) {
    try {
        responseList.add(future.get());
    } catch (Exception e) {
        // do something else.
    }
    return responseList
}

A more appropriate solution with Future is to use a CompletionService, as detailed here.

In Java 8, you should be using CompletableFuture (or Guava's ListenableFuture) for asynchronous tasks. You can still do what I did above, or you can change your code to take full advantage of continuation tasks.

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • So in this example, let's say I have 3 futures and then we are iterating them in for loop, in the try block it will call future.get for first future and then it will wait once that is finished, it will call second second future right? – john Dec 09 '15 at 15:57
  • Is there any way to call all futures get method at once? I am still on Java 7. – john Dec 09 '15 at 16:00
  • That's right. `get` is a blocking call. `CompletionService` makes this a little better. _call them all at once_ would be akki's solution but you have to change around some things in your code and it actually does the same thing internally. – Sotirios Delimanolis Dec 09 '15 at 16:03
  • just trying to under when you say same thing internally what does that mean? So it looks like there is no way to call all futures get method in paralell? – john Dec 09 '15 at 16:17
  • @david Look at the source code of [`invokeAll`](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/concurrent/AbstractExecutorService.java#AbstractExecutorService.invokeAll%28java.util.Collection%29). You can call them in parallel but that's probably more trouble than it's worth. You'll always have to block somewhere anyway. – Sotirios Delimanolis Dec 09 '15 at 16:19
0

You can call Executor service invokeAll method and submit List of Task to it. Executor Service will execute the tasks in parallel.

https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#invokeAll(java.util.Collection)

This would be same as: 1. invoking executeAsync in parallel from the for loop in executeSync method. 2. invoking executeAsync method sequentially from the for loop in executeSync method.

akki
  • 423
  • 2
  • 12
0

There is no reason to execute it in parallel as internally it already runs in parallel. The only thing you need is calling the async method for each of the items and store resulting Futures in the list. After this loop is over you need to go through the list of those Futures, and calling get()on each of them gives the result. These result list you'll return as the return value.

Zbynek Vyskovsky - kvr000
  • 18,186
  • 3
  • 35
  • 43
  • So when we call get on each of the future, that will be sequential call right? I mean call get on first future, then second future and then third future one by one. Is there any way to call all futures get method at once? – john Dec 09 '15 at 16:00
  • It cannot be anything other than sequential as you're appending the result into list which is data structure that must be accessed sequentially. But this operation is very quick as it just checks whether the future is already finished and returns immediately. The execution typically already finished once you call the get. – Zbynek Vyskovsky - kvr000 Dec 09 '15 at 16:33
  • So wait, you mean to say once I add all the futures into the list and before I call get on first future it might have already been finished? If it is true, can you explain that if possible? Still learning so it will help me. – john Dec 09 '15 at 16:36
  • Yes. The task you submit to ExecutorService runs in different thread and is started immediately (or once some of the ExecutorService threads becomes available). – Zbynek Vyskovsky - kvr000 Dec 09 '15 at 17:06
  • I see, so the suggestion given by `SotiriosDelimanolis` will work and it is doing the same thing as you are suggesting right? – john Dec 09 '15 at 17:07