4
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ At a glance, your code looks alright. Note that you only need the Future's timeout mechanism if you actually want to react to a task not completing in the given timeframe. Otherwise, you can just get() the result, which will block until the result is available. \$\endgroup\$ – JimmyB Dec 9 '15 at 17:12
  • \$\begingroup\$ Thanks for confirming. Yeah I need timeout mechanism that's why I have that in place. Also is my understanding wrong about get on future? Will it wait 300 milliseconds for each future to respond back and then call next future? Or all three key object will execute call method in parallel in same 300 milliseconds? \$\endgroup\$ – david Dec 9 '15 at 17:23
5
\$\begingroup\$

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;
}
\$\endgroup\$
5
  • \$\begingroup\$ Thanks for explanation. Why did you remove your first part in the answer? Is my understand wrong about get on future? I am in the impression it will execute call method only when we call get on future no? And also how will I use executeAsync method then from my sync method? \$\endgroup\$ – david Dec 9 '15 at 17:20
  • \$\begingroup\$ Thanks for edit. I have lot of people also calling async method directly by passing single key object. Is there any other way instead of passing keys, timeout and timeunit to async method, we should do all these in sync method and leverage invokeAll in sync may be somehow? I mean I want to keep design of executeAsync method as it is since it's being used by other customer by passing single key object. \$\endgroup\$ – david Dec 9 '15 at 21:28
  • \$\begingroup\$ you can keep your async and inline mine into your sync one \$\endgroup\$ – ratchet freak Dec 9 '15 at 21:32
  • \$\begingroup\$ That's what I am confuse since I am not sure how can I use invokeAll then? If I do as you suggested then will it not look like what I have in my question but without invokeAll? \$\endgroup\$ – david Dec 9 '15 at 21:37
  • \$\begingroup\$ rename my version of executeAsync to dispatchAsync and keep it private. For the async interface returning all the Futures is fine \$\endgroup\$ – ratchet freak Dec 9 '15 at 22:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.