Executor RejectedExecutionHandler

Learn to handle tasks which are submitted to Executor and are rejected because the executor has been shutdown for any reason using 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 form of 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 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 a 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, 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() method of the ThreadPoolExecutor class to indicate to the executor that we want to finish it’s 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 exception is not thrown and handler method is called.

This method is called for every task that is rejected by the executor.

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

Program output.

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.

Drop me your questions in comments section.

Happy Learning !!

Comments

Subscribe
Notify of
guest
1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode