Problem
I have an async method which calls my Task
class while my Task
class does all the work:
private ExecutorService executor = Executors.newFixedThreadPool(10);
@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;
}
Here 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>();
// here keys max size will be three
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(300, TimeUnit.MILLISECONDS));
} catch (Exception e) {
// do something
}
}
return responseList;
}
My intention is to call executeAsync
method in parallel and return back responseList
object. I mean I want to execute call
method of my Task class in parallel for each of the key
object so if I have three key object in keys list, then I want to execute call
method for each key in parallel and then make a List<DataResponse>
object and return it.
Since I don’t want to wait 300 Milliseconds for each key future and then call next key future and then next call third key future. In this way it will wait 300 + 300 + 300 for each of the key. I want to execute call method in parallel in same 300 milliseconds for all three keys.
Does my above code looks right, and are there any better ways of doing this problem? I am still on Java 7.
Solution
An executor service will be able to do a invokeAll
with a timeout and return a set of futures holding all the values or return when the timeout expires.
This means that your executeAsync
should take a set of keys and build all the tasks from those at once and then submit the tasks as a batch:
@Override
public List<Future<DataResponse>> executeAsync(Collection<DataKey> keys, long timeout, TimeUnit unit) {
List<Task> tasks = new ArrayList<>(keys.size());
for(DataKey key : keys){
tasks.add(new Task(key, restTemplate));
}
return executor.invokeAll(tasks, timeout, unit);
}
@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>();
List<Future> futures = executeAsync(keys, 300, TimeUnit.MILLISECONDS);
for (Future<DataResponse> future : futures) {
try {
if(future.isDone())
responseList.add(future.get());
//otherwise future didn't complete in the 300 milliseconds
} catch (Exception e) {
// do something
}
}
return responseList;
}