Tasks to an ExecutorService can be submitted in multiple ways. execute() method is inherited from the Executor interface, which ExecutorService interface extends. The execute() method takes a Runnable lambda expression or instance and completes the task asynchronously. Because the return type of the method is void, it does not tell anything about the result of the task. It is considered a “fire-and-forget” method, as once it is submitted, the results are not directly available to the calling thread.
And there are submit(…) methods defined directly in ExecutorService interface, which, like execute(), can be used to complete tasks asynchronously. Unlike execute(), though, submit() returns a Future object that can be used to determine if the task is complete. It can also be used to return a generic result object after the task has been completed. See table below :
|void execute(Runnable command)||Executes a Runnable task at some point in the future|
|Future<?> submit(Runnable task)||Executes a Runnable task at some point in the future and returns a Future representing the task|
|<T> Future<T> submit(Callable<T> task)||Executes a Callable task at some point in the future and returns a Future representing the pending results of the task|
|<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException||Executes the given tasks, synchronously returning the results of all tasks as a Collection of Future objects, in the same order they were in the original collection|
|<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException||Executes the given tasks, synchronously returning the result of one of finished tasks, cancelling any unfinished tasks|
In practice, using the submit() method is quite similar to using the execute() method, except that the submit() method returns a Future object that can be used to determine whether or not the task has completed execution.
Both invokeAll() and invokeAny() methods take a Collection object containing a list of tasks to execute. Both of these methods also execute synchronously. Synchronous means that unlike the other methods used to submit tasks to a thread executor, these methods will wait until the results are available before returning control to the enclosing program.
The invokeAll() method executes all tasks in a provided collection and returns a List of ordered Future objects, with one Future object corresponding to each submitted task, in the order they were in the original collection. Each task could have completed normally or thrown an exception. invokeAny() method executes a collection of tasks and returns the result of one of the tasks that successfully completes execution, cancelling all unfinished tasks. While the first task to finish is often returned, this behavior is not guaranteed, as any completed task can be returned by this method. Also, invokeAll() method will wait indefinitely until all tasks are complete, while invokeAny() method will wait indefinitely until at least one task completes. The ExecutorService interface also includes overloaded versions of invokeAll() and invokeAny() that take a timeout value and TimeUnit parameter.