Java ExecutorService

Java ExecutorService is an interface that provides methods to execute asynchronous tasks, check the results of those tasks, and shutdown those running tasks when needed.

Java ExecutorService is useful when we want to run multiple tasks concurrently in the background. The ExecutorService provides a pool of threads for processing tasks.

Creating ExecutorService

We can use the Executors factory class from the java.util.concurrent package to create instances of ExecutorService. The Executors class provides several factory methods for creating instances of ExecutorService. How you want to create an instance of ExecutorService depends on what you want your code to do.

Here are some examples of creating instances of ExecutorService:


ExecutorService fixedPoolExecutor = Executors.newFixedThreadPool(10);
ExecutorService chachedPoolExecutor = Executors.newCachedThreadPool();
ExecutorService singleExecutor = Executors.newSingleThreadExecutor();
ExecutorService scheduledPoolExecutor = Executors.newScheduledThreadPool(10);

ExecutorService Methods to Run Tasks

The following are the methods for delegating task to ExecutorService:

  • execute(Runnable command) - Executes a task in a new thread, in a new pooled thread, or in the calling thread.
  • Example:
    
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.execute(new Runnable() {
        @Override
        public void run() {
            System.out.println("My task here");
        }
    });
    executorService.shutdown();
    
  • submit(Runnable task) - Submits a Runnable task that returns a Future object representing the result of the task upon completion.
  • Example:
    
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future future = executorService.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("My task here");
        }
    });
    future.get(); // returns null on successful completion
    executorService.shutdown();
    
  • submit(Callable<T> task) - Submits a task that returns a value. The method returns a Future object representing the result of the task upon completion.
  • Example:
    
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future future = executorService.submit(new Callable<Object>() {
        @Override
        public Object call() throws Exception {
            System.out.println("Asynchronous task");
            return "You can return your object here";
        }
    });
    future.get();
    executorService.shutdown();
    
  • invokeAll(Collection<? extends Callable<T>> tasks) - This method runs a list of tasks and returns a list of Futures representing the results and status of the tasks after completely running all the given tasks.
  • Example:
    
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    List<Callable<String>> callableTaskList = new ArrayList<>();
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 1";
        }
    });
     
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 2";
        }
    });
        
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 3";
        }
    });
        
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 4";
        }
    });
        
    List<Future<String>> futureList = executorService.invokeAll(callableTaskList);
    for (Future<String> future : futureList) {
        future.get();
    }
    
    executorService.shutdown();
    
  • invokeAny(Collection<? extends Callable<T>> tasks) - This method executes the given task and returns the result of one task that has completed successfully. Incomplete tasks are cancelled upon any error.
  • Example:
    
    ExecutorService executorService = Executors.newFixedThreadPool(4);
    List<Callable<String>> callableTaskList = new ArrayList<>();
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 1";
        }
    });
     
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 2";
        }
    });
        
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 3";
        }
    });
        
    callableTaskList.add(new Callable<String>() {
        @Override
        public String call() throws Exception {
            return "Asynchronous task 4";
        }
    });
        
    String result = executorService.invokeAny(callableTaskList);
    
    executorService.shutdown();
    

Cancel Task

A running task can be cancelled by calling the cancel() method on the Future object. The cancel attempt will not work if the task has already been completed or cancelled.


future.cancel(true);

ExecutorService Shutdown

Following are some of the methods to shutdown ExecutorService:

  • shutdown() - This method intiates an orderly shutdown and does not wait for the previously submitted tasks to complete.
  • 
    executorService.shutdown();
    
  • shutdownNow() - This method attempts to shutdown all running tasks and returns a list of all tasks that were awaiting for execution. This method does not wait for previously submitted tasks to complete.
  • 
    executorService.shutdownNow();
    
  • awaitTermination(long timeout, TimeUnit unit) - This method blocks all types of termination request until all submitted tasks have completed execution.
  • 
    executorService.shutdown();
    executorService.awaitTermination(60, TimeUnit.SECONDS);