Java Thread Tutorial - Java Executor








The framework provides a way to separate task submission from task execution.

The Executor interface in the java.util.concurrent package is the foundation for the executor framework.

It is an interface with only one method, as shown:

public interface  Executor  {
    void  execute  (Runnable command);
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//from  w w w . j a  va2s  .  co m
class RunnableTask implements Runnable {
  private int taskId;
  private int loopCounter;

  public RunnableTask(int taskId, int loopCounter) {
    this.taskId = taskId;
    this.loopCounter = loopCounter;
  }
  public void run() {
    for (int i = 1; i <= loopCounter; i++) {
      try {
        System.out.println("Task #" + this.taskId + "  - Iteration #" + i);
        Thread.sleep(1000);
      } catch (Exception e) {
        System.out.println("Task #" + this.taskId
            + "  has  been  interrupted.");
        break;
      }
    }
  }
}
public class Main {
  public static void main(String[] args) {
    final int THREAD_COUNT = 3;
    final int LOOP_COUNT = 3;
    final int TASK_COUNT = 5;

    // Get an executor with three threads in its thread pool
    ExecutorService exec = Executors.newFixedThreadPool(THREAD_COUNT);

    // Create five tasks and submit them to the executor
    for (int i = 1; i <= TASK_COUNT; i++) {
      RunnableTask task = new RunnableTask(i, LOOP_COUNT);
      exec.submit(task);
    }
    exec.shutdown();
  }
}

The code above generates the following result.





Result-Bearing Tasks

To get the result of a task when it is complete, use an instance of the Callable interface.

The type parameter V is type of the result of the task.

The Callable interface has a call() method. It can return a value of any type.

It allows you to throw an exception. It is declared as follows:

public interface  Callable<V>  {
    V  call() throws   Exception;
}
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*from  ww w. j  a va 2s.c o m*/
class CallableTask implements Callable<Integer> {
  private int taskId;

  public CallableTask(int taskId) {
    this.taskId = taskId;
  }

  public Integer call() throws InterruptedException {
    int total = taskId;
    try {
      System.out.println("Task #" + this.taskId);
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      System.out.println("Task #" + this.taskId
          + "  has  been  interupted.");
      throw e;
    }
    total+=taskId;
    return total;
  }
}

public class Main {
  public static void main(String[] args) throws Exception {
    // Get an executor with three threads in its thread pool
    ExecutorService exec = Executors.newFixedThreadPool(3);
    CallableTask task = new CallableTask(1);
    // Submit the callable task to executor
    Future<Integer> submittedTask = exec.submit(task);

    Integer result = submittedTask.get();
    System.out.println("Task's total  sleep time: " + result + "  seconds");
    exec.shutdown();
  }
}

The code above generates the following result.





Scheduling a Task

The executor framework lets you schedule a task that will run in future.

import java.time.LocalDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/*from ww  w.  j  a  va  2  s. c  o  m*/
class ScheduledTask implements Runnable {
  private int taskId;

  public ScheduledTask(int taskId) {
    this.taskId = taskId;
  }

  public void run() {
    LocalDateTime currentDateTime = LocalDateTime.now();
    System.out.println("Task #" + this.taskId + "  ran  at "
        + currentDateTime);
  }
}
public class Main {
  public static void main(String[] args) {
    // Get an executor with 3 threads
    ScheduledExecutorService sexec = Executors.newScheduledThreadPool(3);

    ScheduledTask task1 = new ScheduledTask(1);
    ScheduledTask task2 = new ScheduledTask(2);

    // Task #1 will run after 2 seconds
    sexec.schedule(task1, 2, TimeUnit.SECONDS);

    // Task #2 runs after 5 seconds delay and keep running every 10 seconds
    sexec.scheduleAtFixedRate(task2, 5, 10, TimeUnit.SECONDS);

    try {
      TimeUnit.SECONDS.sleep(60);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    sexec.shutdown();
  }
}

The code above generates the following result.

Handling Uncaught Exceptions in a Task Execution

The executor framework handles occurrences of any uncaught exception during task execution nicely.

If you execute a Runnable task using the execute() method of the Executor object, any uncaught runtime exceptions will halt the task execution, and the exception stack trace will be printed on the console.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//  ww w.  j a  v a 2s  . c  o  m
public class Main {
  public static void main(String[] args) {
    Runnable badTask = () -> {
      throw new RuntimeException(
          "Throwing exception  from  task execution...");
    };

    ExecutorService exec = Executors.newSingleThreadExecutor();
    exec.execute(badTask);
    exec.shutdown();
  }
}

The code above generates the following result.

Handle exception in Callable tasks

The following code shows how to handle exception in Callable tasks.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*  ww  w  . j  a va2 s.  c om*/
public class Main {
  public static void main(String[] args) {
    Callable<Object> badTask = () -> {
      throw new RuntimeException(
          "Throwing exception from task execution...");
    };
    ExecutorService exec = Executors.newSingleThreadExecutor();
    Future submittedTask = exec.submit(badTask);
    try {
      Object result = submittedTask.get();
    } catch (ExecutionException e) {
      System.out.println(e.getMessage());
      System.out.println(e.getCause().getMessage());
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    exec.shutdown();
  }
}

Executor's Completion Service

To get the result from the submitted tasks to an executor, use the completion service of the executor.

It is represented by an instance of the CompletionService interface.

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/*from   w  w  w. j a v a2  s  .  c  om*/
class MyResult {
  private int taskId;
  private int result;

  public MyResult(int taskId, int result) {
    this.taskId = taskId;
    this.result = result;
  }

  public int getTaskId() {
    return taskId;
  }

  public int getResult() {
    return result;
  }

  public String toString() {
    return "Task  Name: Task  #" + taskId + ", Task  Result:" + result
        + "  seconds";
  }
}

class SleepingTask implements Callable<MyResult> {
  private int taskId;
  private int loopCounter;
  public SleepingTask(int taskId, int loopCounter) {
    this.taskId = taskId;
    this.loopCounter = loopCounter;
  }

  public MyResult call() throws InterruptedException {
    int totalSleepTime = 0;
    for (int i = 1; i <= loopCounter; i++) {
      try {
        System.out.println("Task #" + this.taskId + "  - Iteration #"
            + i);
        Thread.sleep(1000);
        totalSleepTime = totalSleepTime + 1000;
      } catch (InterruptedException e) {
        System.out.println("Task #" + this.taskId
            + "  has  been  interupted.");
        throw e;
      }
    }
    return new MyResult(taskId, totalSleepTime);
  }
}

public class Main {
  public static void main(String[] args) throws Exception {
    // Get an executor with three threads in its thread pool
    ExecutorService exec = Executors.newFixedThreadPool(3);

    // Completed task returns an object of the TaskResult class
    ExecutorCompletionService<MyResult> completionService = new ExecutorCompletionService<>(
        exec);
    for (int i = 1; i <= 5; i++) {
      SleepingTask task = new SleepingTask(i, 3);
      completionService.submit(task);
    }
    for (int i = 1; i <= 5; i++) {
      Future<MyResult> completedTask = completionService.take();
      MyResult result = completedTask.get();
      System.out.println("Completed a  task - " + result);
    }
    exec.shutdown();
  }
}

The code above generates the following result.