Multithreading: Analysis of Four Types of Thread Pool Rejection Strategies

insert image description here

1. The rejection strategy of the thread pool

  • In the thread pool, there are three important parameters that determine the rejection strategy:
    • corePoolSize - the number of core threads, which is the minimum number of threads
    • workQueue - the blocking queue
    • maximumPoolSize - the maximum number of threads

Thread pool execution principle

  1. When a thread comes in, it will first occupy corePoolSize (number of core threads)
  2. When the number of submitted tasks is greater than corePoolSize, the tasks will be placed in the workQueue blocking queue first. If the number of queues exceeds the number of threads in the thread pool, the number of threads in the thread pool will be expanded until the maximum number of threads configured by maximumPoolSize is reached.
  3. At this point, any redundant tasks will trigger the rejection policy of the thread pool
  • To sum up, in one sentence, when the number of submitted tasks is greater than (workQueue.size() + maximumPoolSize), the rejection strategy of the thread pool will be triggered

2. Deny policy definition

  • The rejection strategy provides the top-level interface RejectedExecutionHandler, where the method rejectedExecution is to customize the execution logic of the specific rejection strategy

jdk provides four rejection strategies by default

AbortPolicy (throws an exception, aborts the task)

  • Throws RejectedExecutionException exception information. The default rejection policy for the thread pool. The thrown exception must be handled properly, otherwise it will interrupt the current execution process and affect the execution of subsequent tasks

DiscardPolicy (discard directly, nothing else)

DiscardOldestPolicy (discard the oldest task in the queue, add a new task)

When the rejection policy is triggered, as long as the thread pool is not closed, the oldest task in the blocking queue workQueue is discarded and a new task is added

CallerRunsPolicy

  • Execute the task using the calling thread. When the rejection policy is triggered, as long as the thread pool is not closed, the calling thread is used to run the task directly. Generally, the concurrency is relatively small, the performance requirements are not high, and failure is not allowed. However, since the caller runs the task by himself, if the task submission speed is too fast, the program may be blocked, and the performance efficiency will inevitably suffer a large loss.

3. Case demonstration

AbortPolicy

public static void main(String[] args) throws Exception{
    
    
		//核心线程数
        int corePoolSize = 5;
        //最大线程数
        int maximumPoolSize = 10;
        //存活时间
        long keepAliveTime = 5;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(10);
        //测试不同的拒绝策略时,可以调用不同的方法
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);
        for(int i=0; i<100; i++) {
    
    
            try {
    
    
                executor.execute(new Thread(() -> log.info(Thread.currentThread().getName() + " is running")));
            } catch (Exception e) {
    
    
                log.error(e.getMessage(),e);
            }
        }
        executor.shutdown();
    }

Analysis: executor.execute() submits the task, because RuntimeException will be thrown, if there is no try.catch to handle the exception information, the processing flow of the caller will be interrupted, and subsequent tasks will not be executed (cannot run 100). You can test it yourself, and it is easy to see it in the console.

CallerRunsPolicy

The main code is the same as above, replace the rejection policy:

RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

Analysis: After running, what can be seen in the console console is that part of the data will be printed, and the display is "main is running", that is, it is processed by the calling thread.

DiscardPolicy

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

Analysis: Discard the task directly. In actual operation, there will not be 100 pieces of information printed out.

DiscardOldestPolicy

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();

Analysis: In actual operation, the printed information will be less than 100 pieces.

Four. Summary

  • The four rejection strategies are independent of each other, and the choice of which strategy to implement depends on specific business scenarios. In actual work, when using ExecutorService directly, the default defaultHandler is used, which is the AbortPolicy strategy.
  • Personally, I recommend that you generally use the CallerRunsPolicy policy in production, because at least it will not discard threads, and the other three are all thread lost. Of course, it is still necessary to analyze the specific business and use the thread pool reasonably.
  • Reference blog:
    https://www.cnblogs.com/eric-fang/p/11584142.html
    https://blog.csdn.net/suifeng629/article/details/98884972

Guess you like

Origin blog.csdn.net/Mango_Bin/article/details/129647858