Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Whether to use invokeAll or submit - java Executor service

I have a scenario where I have to execute 5 thread asynchronously for the same callable. As far as I understand, there are two options:

1) using submit(Callable)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = new ArrayList<>();
for(Callable callableItem: myCallableList){
    futures.add(executorService.submit(callableItem));
}

2) using invokeAll(Collections of Callable)

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));
  1. What should be the preferred way?
  2. Is there any disadvantage or performance impact in any of them compared to the other one?
like image 519
Ankit Avatar asked Dec 23 '15 17:12

Ankit


People also ask

What is the difference between Executor execute and Executor submit in Java?

1) The submit() can accept both Runnable and Callable tasks but execute() can only accept the Runnable task. 2) The submit() method is declared in the ExecutorService interface while the execute() method is declared in the Executor interface.

What does invokeAll do?

invokeAll() method executes the given list of Callable tasks, returning a list of Future objects holding their status and results when all complete. It is overloaded method and is in two forms.

What does Executor submit do in Java?

The Java ExecutorService submit(Runnable) method also takes a Runnable implementation, but returns a Future object. This Future object can be used to check if the Runnable has finished executing. Here is a Java ExecutorService submit() example: Future future = executorService.

Why do we need Executor service in Java?

The ExecutorService helps in maintaining a pool of threads and assigns them tasks. It also provides the facility to queue up tasks until there is a free thread available if the number of tasks is more than the threads available.


Video Answer


3 Answers

Option 1 : You are submitting the tasks to ExecutorService and you are not waiting for the completion of all tasks, which have been submitted to ExecutorService

Option 2 : You are waiting for completion of all tasks, which have been submitted to ExecutorService.

What should be the preferred way?

Depending on application requirement, either of them is preferred.

  1. If you don't want to wait after task submit() to ExecutorService, prefer Option 1.
  2. If you need to wait for completion of all tasks, which have been submitted to ExecutorService, prefer Option 2.

Is there any disadvantage or performance impact in any of them compared to the other one?

If your application demands Option 2, you have to wait for completion of all tasks submitted to ExecutorService unlike in Option 1. Performance is not criteria for comparison as both are designed for two different purposes.

And one more important thing: Whatever option you prefer, FutureTask swallows Exceptions during task execution. You have to be careful. Have a look at this SE question: Handling Exceptions for ThreadPoolExecutor

With Java 8, you have one more option: ExecutorCompletionService

A CompletionService that uses a supplied Executor to execute tasks. This class arranges that submitted tasks are, upon completion, placed on a queue accessible using take. The class is lightweight enough to be suitable for transient use when processing groups of tasks.

Have a look at related SE question: ExecutorCompletionService? Why do need one if we have invokeAll?

like image 67
Ravindra babu Avatar answered Oct 08 '22 18:10

Ravindra babu


EDIT:

There is actually a difference between them. For some reason, invokeAll() will call get() for each future produced. Thus, it will wait the tasks to finish and that is why it may throw InterruptedException (while submit() throws nothing).

That's the Javadoc for the invokeAll() method:

Executes the given tasks, returning a list of Futures holding their status and results when all complete.

So, both strategies basically do the same, but if you call invokeAll() you'll be blocked until all tasks are done.


Original (incomplete) answer:

The invokeAll() method is there exactly for situations like these. You should definitely use it.

You don't really need to instantiate that List, though:

ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<String>> futures = executorService.invokeAll(myCallableList));

This should be enough, and it looks way cleaner than the first alternative.

like image 24
Fred Porciúncula Avatar answered Oct 08 '22 20:10

Fred Porciúncula


Suppose you have a task whose result depends on number of independentaly executable tasks. But for initial task to complete you only have limited time. Like its an API call.

So for example you have 100ms for top level task to complete and there are 10 dependant tasks as well. For that if you are using a submit here how the code will look like.

List<Callable> tasks = []// assume contains sub tasks
List<Future> futures = [] 
for(Callable task: tasks) {
   futures.add(service.submit(task));
}

for(Future futute: futures) {
    future.get(100, TimeUnit.MILLISECONDS);
}

So if each of sub tasks took exaclty 50ms to complete the above piece of code would take 50 ms. But if each of sub tasks took 1000 ms to complete the above would take 100 * 10 = 1000 ms or 1s. This is making difficult to compute the total time to be less than 100ms for all subtasks.

invokeAll method helps us in such scenario

List<Futures> futures = service.invokeall(tasks, 100, TimeUnit.MILLISECONDS)
for(Future future: futures) {
   if(!future.isCancelled()) {
       results.add(future.get());
   }
}

This way the maximum time it would take is inly 100 ms even if individual of subtasks took more than that.

like image 1
parampreet bawa Avatar answered Oct 08 '22 19:10

parampreet bawa