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.
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.
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.
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.
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(); } }
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.