Search⌘ K
AI Features

RejectedExecutionException

Explore the causes and handling of RejectedExecutionException in Java executor services. Understand when this runtime exception is thrown due to executor shutdown or resource exhaustion, and learn how thread pool size and queue capacity affect task acceptance during multithreaded execution.

We'll cover the following...

If you are interviewing, consider buying our number#1 course for Java Multithreading Interviews.

Overview

Rather than creating individual threads for executing jobs in parallel, we can leverage Java’s executor classes that abstract away thread scheduling and management from the user and leave the user to focus on submitting Runnable or Callable tasks/commands. Some of the classes implementing the Executor and ExecutorService interfaces are as follows:

  • AbstractExecutorService
  • ForkJoinPool
  • ScheduledThreadPoolExecutor
  • ThreadPoolExecutor

The Executor interface defines a method execute while the ExecutorService defines overloaded versions of the submit method. Both these methods throw the runtime exception RejectedExecutionException when implemented by the various executor classes. The RejectedExecutionException exception is thrown when a task can’t be accepted by the executor for execution. Generally there are two scenarios when a task is rejected for execution: The executor service has already been shut down. When the executor service has exhausted resources and can’t take on any more task submissions. For instance in the case of the ThreadPoolExecutor, the RejectedExecutionException is thrown when the executor service uses a queue with a defined maximum capacity and a defined maximum number of threads for the thread pool and both resources have reached capacity.

Example

Consider the program below that attempts to submit a task for execution to a fixed-size thread pool executor, after shutdown() has already been invoked. The executor throws the RejectedExecutionException.

Java
import java.util.concurrent.*;
class Demonstration {
public static void main( String args[] ) {
// create a executor service
ExecutorService executor = Executors.newFixedThreadPool(5);
// shutdown the executor
executor.shutdown();
// attempt to execute a Runnable after the ExecutorService has been shutdown
executor.execute(new Runnable() {
@Override
public void run() {
// dummy task
}
});
}
}

As a second example, consider the program below that uses an instance of ThreadPoolExecutor as the executor service to submit tasks for execution. We instantiate the instance with a thread pool size of 10 and a queue to hold submitted tasks of size 10 too. The first 10 tasks submitted are all executed by the 10 available threads and the queue remains empty. However, the next ten tasks submitted fill-up the queue and on submitting the 21st task the executor throws RejectedExecutionException.

Java
import java.util.concurrent.*;
class Demonstration {
public static void main( String args[] ) {
// Create a ThreadPoolExecutor with maximum of 10 threads and a queue that can hold at most 10 tasks.
ExecutorService executorService = new ThreadPoolExecutor(5, 10, 1, TimeUnit.HOURS,
new LinkedBlockingQueue<Runnable>(10));
// declar outstide the try loop to determine which task gets rejected.
int i = 0;
try {
// The first ten tasks should be executed by the ten threads in the pool. The next ten tasks should
// fill up the queue while the 21st task should cause the executor service to throw the RejectedExecutionException.
for (; i < 21; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
// Each task simulates work by sleeping for a minute.
Thread.sleep(1000 * 60);
} catch (InterruptedException ie) {
// This statement will print on the console when we shutdown the executor before the tasks complete.
System.out.println("Task executed by thread " + Thread.currentThread().getName() + " has been interrupted");
}
}
});
}
// This statement never gets printed.
System.out.println("Submitted all tasks");
} catch (RejectedExecutionException ree) {
System.out.println("Task " + i + " was rejected");
} finally {
// force the executor to stop all executing tasks and shutdown.
executorService.shutdownNow();
}
}
}

In the above program if you change the for loop limit from 21 to 20 on line#16, the program would run without experiencing any exceptions.