ExecutorService in Java is an interface that is part of the java.util.concurrent package. This Java concurrency utility helps to execute asynchronous tasks concurrently. Using the ExecutorService interface, we can separate the task creation and task execution process. It is a subinterface of the ExecutorFramework. In this tutorial, we will discuss in detail Java ExecutorService, how to instantiate a service along with its methods and different examples.
Table of Contents
ExecutorService Framework
In a multithreaded environment, we may have to work on multiple threads concurrently. Sometimes, this can be very difficult and hence may need a framework to handle the thread creation and execution separately. For this, Java 1.5 introduced the concept of the ExecutorService framework. This framework contains 3 main interfaces namely Executor, ExecutorService, and ThreadPoolExecutor.
The main purpose of an ExecutorService is to manage a number of threads and also assign tasks to the threads. In case, the number of tasks is more than the number of threads, it queues up the tasks until any thread is available for execution.
Methods of ExecutorService
Below are the main methods of the ExecutorService interface.
Method | Description |
---|---|
boolean awaitTermination(long timeout, TimeUnit timeunit) | Blocks until all tasks have completed execution after shutdown or after a particular timeout. |
void execute(Runnable command) | Executes the given command |
List<Future> invokeAll(Collections task) | Executes the given tasks and returns the list of futures holding their status and results when complete |
List<Future> invokeAll(Collections task, long timeout, TimeUnit timeunit) | Executes the given tasks and returns the list of futures holding their status and results when complete or when timeout occurs |
T invokeAny(Collections tasks) | Executes the given task and returns the result of the completed task |
T invokeAny(Collections task, long timeout, TimeUnit timeunit) | Executes the given task and returns the result of the completed task before the timeout occurs. |
boolean isShutDown() | Returns true if the executor is shutdown |
boolean isTerminated() | Returns true if the execution of all task is complete after shutdown |
void shutDown() | Initiates a shutdown for all the submitted tasks |
List shutDownNow() | Attempts to stop all actively executing tasks and returns a list of all awaiting task |
Future submit(Callable task) | Submits a value returning task for execution and returns the future of pending result task |
Future submit(Runnable task) | Submits a Runnable task for execution. |
Future submit(Runnable task, T result) | Submits a Runnable task and returns the Future representing the task. |
Creating an ExecutorService instance
We can create an ExecutorService instance in the below 3 ways:
- Single thread: Creates a single thread instance using an ExecutorService
- Thread pool: Creates a pool of threads by specifying the number of threads in the parameter
- A scheduled pool of threads: Creates a scheduled pool of threads
ExecutorService exec = Executors.newSingleThreadExecutor(); ExecutorService exec = Executors.newFIxedThreadPool(int count); ExecutorService exec = Executors.newScheduledThreadPool(int count);
Assign task using Java ExecutorService execute() method
The below example shows how to execute an asynchronous task using the execute()
method of the ExecutorService. In this example, we create a single thread instance using the newSingleThreadExecutor. The execute()
method takes the Runnable object as a parameter. Finally, after the task execution, we can check if the ExecutorService is shutdown using the isShutDown()
method.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorServiceDemo { public static void main(String[] args) { ExecutorService exec = Executors.newSingleThreadExecutor(); exec.execute(new Runnable() { @Override public void run() { System.out.println("Example of execute method"); } }); exec.shutdown(); System.out.println("Is ExecutorService Shutdown: " + exec.isShutdown()); } }
Example of execute method Is ExecutorService Shutdown: true
Assign task using the Java ExecutorService submit() method
This is an example of the ExecutorService submit()
method that accepts the Runnable task as a parameter.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorServiceDemo { public static void main(String[] args) { ExecutorService exec = Executors.newSingleThreadExecutor(); exec.submit(new Runnable() { @Override public void run() { System.out.println("Example of submit method"); } }); exec.shutdown(); } }
Example of submit method
Assign task using the ExecutorService invokeAny() method
This example shows how to use the invokeAny()
method of the Java ExecutorService. We need to pass the Callable object or action as a parameter to the invokeAny()
method. Whichever callable action it executes, it returns the result of the corresponding task.
import java.util.HashSet; import java.util.Set; import java.util.concurrent.*; public class ExecutorServiceDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService exec = Executors.newSingleThreadExecutor(); Set<Callable<String>> c = new HashSet<Callable<String>>(); c.add(new Callable<String>() { public String call() throws Exception { return "Callable Task1"; } }); c.add(new Callable<String>() { public String call() throws Exception { return "Callable Task2"; } }); String value = exec.invokeAny(c); System.out.println(value); exec.shutdown(); } }
Callable Task1
Assign task using the ExecutorService invokeAll() method
This example shows how to use the Java ExecutorService interface invokeAll()
method that executes all the Callable actions. It returns the result of the task execution in the form of Future object.
import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.*; public class ExecutorServiceDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService exec = Executors.newSingleThreadExecutor(); Set<Callable<String>> c = new HashSet<Callable<String>>(); c.add(new Callable<String>() { @Override public String call() throws Exception { return "Callable Task1"; } }); c.add(new Callable<String>() { @Override public String call() throws Exception { return "Callable Task2"; } }); List<Future<String>> l = exec.invokeAll(c); for(Future<String> f: l) System.out.println(f.get()); exec.shutdown(); } }
Callable Task1 Callable Task2
Important points on ExecutorService
- Always remember to shutdown the ExecutorService once the task is completed. Never keep the unused ExecutorService alive.
- Make proper use of the Threadpool capacity while using a fixed-length thread pool
- We cannot call the get() method of the Future interface after canceling the task. This will throw a CancellationException.
- Use relevant timeouts wherever required to avoid unnecessary blocking time.