11 What Are the Four Rejection Policies of Thread Pools

11 What Are the Four Rejection Policies of Thread Pools #

In this lesson, we will mainly learn about the 4 default rejection policies of thread pools.

Rejection Occurrence #

First, when creating a new thread pool, you can specify its rejection policy, for example:

newThreadPoolExecutor(5, 10, 5, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
   new ThreadPoolExecutor.DiscardOldestPolicy());

In order to reject tasks according to our strategy when necessary, what is the rejection occurrence? The thread pool will reject newly submitted tasks in the following two situations:

  • The first situation is when we call the shutdown method to close the thread pool. Even if there are tasks that have not been completed inside the thread pool at this time, if new tasks are submitted to the thread pool, they will be rejected because the thread pool has been closed.
  • The second situation is when the thread pool is unable to continue processing newly submitted tasks, meaning that the work is already very saturated.

Let’s talk about the second situation in detail, which is rejection due to work saturation. For example, create a new thread pool, use an ArrayBlockingQueue with a capacity limit of 10 as the task queue, and specify the core thread count of the thread pool as 5 and the maximum thread count as 10. Assume that at this time, 20 time-consuming tasks are submitted. In this case, the thread pool will first create the number of core threads, which is 5 threads, to execute tasks. Then it will put tasks in the queue. After the queue with a capacity of 10 is filled, it will continue to create new threads until the maximum thread count of 10 is reached. At this point, there are a total of 20 tasks in the thread pool, with 10 tasks being executed by 10 threads, and 10 tasks waiting in the task queue. Since the maximum thread count of the thread pool is 10, no more threads can be added to help process tasks. This means that the thread pool is saturated at this time, and when new tasks are submitted, they will be rejected.

img

Let’s analyze the above situation based on the diagram. First, look at the queue section in the upper right. You can see that the queue is already full, and each thread below the queue in the figure is working, and the number of threads has reached the maximum value of 10. If new tasks are submitted at this time, the thread pool will reject them because it does not have the ability to continue processing newly submitted tasks.

Now that we understand the occurrence of task rejection in thread pools, how do we choose the rejection policy correctly? Java provides us with 4 default rejection policies in the ThreadPoolExecutor class to deal with different scenarios. All of them implement the RejectedExecutionHandler interface, as shown in the following figure:

img

Next, we will explain these 4 rejection policies in detail.

Rejection Policies #

  • The first rejection policy is AbortPolicy. When tasks are rejected using this policy, it throws a RuntimeException of type RejectedExecutionException, letting you know that the task has been rejected. Then you can choose to retry or give up the submission based on your business logic.
  • The second rejection policy is DiscardPolicy. As the name suggests, this rejection policy directly discards newly submitted tasks without giving you any notification. It has a certain risk because when we submit a task, we don’t know that it will be discarded, which may result in data loss.
  • The third rejection policy is DiscardOldestPolicy. If the thread pool is not closed and has no capacity to execute, it will discard the head node of the task queue. Usually, it discards the oldest task in terms of survival time. The difference between this policy and the second one is that it discards the longest living task in the queue, rather than the most recently submitted task. This way, space can be made for newly submitted tasks, but it also has a certain risk of data loss.
  • The fourth rejection policy is CallerRunsPolicy. This policy is relatively more comprehensive. When a new task is submitted and the thread pool is not closed and has no ability to execute, it assigns this task to the thread that submitted the task for execution. In other words, the thread that submitted the task is responsible for executing it. This has two advantages:
    • The first advantage is that newly submitted tasks will not be discarded, which will prevent business losses.
    • The second advantage is that since the thread that submitted the task is responsible for executing it, and executing the task is time-consuming, during this period of time, the thread that submitted the task is occupied and will not submit new tasks, slowing down the task submission speed, which is equivalent to negative feedback. During this period, the threads in the thread pool can fully utilize this time to execute some tasks, freeing up some space, which is equivalent to giving the thread pool a buffer period.