Executor RejectedExecutionHandler
1. When tasks get rejected
Remember, when we finish the execution of an executor, we use the shutdown() method. The executor waits for the completion of tasks that are either running or waiting for their execution. Then, it shuts down the executor.
If we send a task to an executor between invoking the shutdown() method and the end of its execution, the task will be rejected. This is because the executor no longer accepts new tasks.
The ThreadPoolExecutor class provides a mechanism in the form of a callback method, which is called when a task is rejected.
2. RejectedExecutionHandler example
2.1. Create task to execute
Let’s create a demo task which we will execute using executor framework. This is a simple task which print some statement and simulate a random delay as it is doing something important.
class Task implements Runnable { private final String name; public Task(String name) { this.name = name; } @Override public void run() { System.out.printf("%s: Task %s: Created on: %s\n", Thread.currentThread().getName(), name, LocalDateTime.now()); try { Long duration = (long)(Math.random()*10); System.out.printf("%s: Task %s: Doing a task during %d seconds\n", Thread.currentThread().getName(), name, duration); TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("%s: Task %s: Finished on: %s\n", Thread.currentThread().getName(), name, LocalDateTime.now()); } @Override public String toString() { return "[name=" + name + "]"; } }
2.2. Implement RejectedExecutionHandler
Create a class and implement interface RejectedExecutionHandler. It’s method rejectedExecution() is responsible for handling the tasks which get rejected from
ThreadPoolExecutor
class RejectedTaskHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.out.printf("RejectedTaskHandler: The task %s has been rejected", r.toString()); } }
2.3. Add handler to executor and test it
Let’s create an executor instance and verify whether this handler is called when a thread is rejected. Here, we have created a cached thread pool using the Executors.newFixedThreadPool() method in order to create the executor.
Notice, we used the Runtime.availableProcessors() method that returns the number of processors available to JVM. Normally, this number matches the number of cores of the computer. Initially, the thread pool will have this number of threads. In my laptop, it is 4.
After creating the executor, we send tasks of the Runnable type for execution using the execute()
method.
By default, if the executor doesn’t have tasks to execute, it continues waiting for new tasks and doesn’t end its execution. JVM does not stop after all the tasks are executed. Use shutdown()
a method of the ThreadPoolExecutor
class to indicate to the executor that we want to finish its execution.
After we call the shutdown()
method, if we try to send another task to the executor, it will be rejected. By default, the executor will throw a RejectedExecutionException exception. If we add a RejectedExecutionHandler, then the exception is not thrown and the handler method is called.
This method is called for every task that is rejected by the executor.
Main.java import java.time.LocalDateTime; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args) { final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors .newFixedThreadPool(Runtime.getRuntime().availableProcessors()); RejectedTaskHandler handler=new RejectedTaskHandler(); executor.setRejectedExecutionHandler(handler); for (int i=0; i<10; i++) { Task task=new Task("Task-"+i); executor.execute(task); } //shut down the executor so that new tasks will be rejected executor.shutdown(); Task task = new Task("Rejected task"); executor.execute(task); } }
Console RejectedTaskHandler: The task [name=Rejected task] has been rejected pool-1-thread-2: Task Task-1: Created on: 2019-05-22T15:13:51.147 pool-1-thread-3: Task Task-2: Created on: 2019-05-22T15:13:51.147 pool-1-thread-1: Task Task-0: Created on: 2019-05-22T15:13:51.147 pool-1-thread-1: Task Task-0: Doing a task during 6 seconds pool-1-thread-4: Task Task-3: Created on: 2019-05-22T15:13:51.147 pool-1-thread-4: Task Task-3: Doing a task during 5 seconds pool-1-thread-3: Task Task-2: Doing a task during 7 seconds pool-1-thread-2: Task Task-1: Doing a task during 4 seconds ...
Clearly, the handler method of RejectedTaskHandler
is invoked. Here we can add our own custom logic to handle this task as per requirements.
Leave a Reply