Detailed explanation of java thread pool ThreadPoolExecutor class -- common task queue

Posted by lapith on Mon, 10 Jan 2022 22:03:05 +0100

In Alibaba java development manual, it is written that thread pools are not allowed to be created using Executors, but through ThreadPoolExecutor.
The disadvantages of thread pool objects returned by Executors are as follows: 1) FixedThreadPool and SingleThreadPool:
The allowed request queue length is integer MAX_ Value, which may accumulate a large number of requests, resulting in OOM.
2) CachedThreadPool and ScheduledThreadPool:
The number of threads allowed to create is integer MAX_ Value, a large number of threads may be created, resulting in OOM.
How to use the ThreadPoolExecutor class.
The first is the constructor of ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

Constructor parameter meaning:
corePoolSize: Specifies the number of threads in the thread pool, which determines whether the added task is to open up a new thread to execute or put it into the workQueue task queue.
maximumPoolSize: Specifies the maximum number of threads in the thread pool. This parameter determines the maximum number of threads that the thread pool will open up according to the type of workQueue task queue you use.
keepAliveTime: when the number of idle threads in the thread pool exceeds the corePoolSize, how long will the redundant threads be destroyed.
Unit: the unit of keepAliveTime.
workQueue: task queue, which is added to the thread pool but has not been executed; It is generally divided into direct submission queue, bounded task queue, unbounded task queue and priority task queue.
threadFactory: thread factory, which is used to create threads. Generally, it can be used by default.
handler: reject policy; How to reject a task when there are too many tasks to handle
1, workQueue task queue
It is generally divided into direct submission queue, bounded task queue, unbounded task queue and priority task queue
1. Direct submission queue: it is set as SynchronousQueue queue. SynchronousQueue is a special BlockingQueue. It has no capacity and will be blocked every time an insert operation is performed.

    public class ThreadTest implements Runnable{
        private int num;
        ThreadTest(int i){
            this.num = i;
        }
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"task"+num);
        }
    }
    @Test
    public void threadTest(){
        ExecutorService pool = new ThreadPoolExecutor(1, 4, 2000, TimeUnit.MILLISECONDS, new SynchronousQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
        for(int i = 1;i<10;i++){
            pool.execute(new ThreadTest(i));
        }
    }

Operation results:
pool-1-thread-1 task 1
pool-1-thread-3 task 3
pool-1-thread-2 task 2
pool-1-thread-4 task 4

java.util.concurrent.RejectedExecutionException: Task com.Alm.blog.service.controller.BlogController$ThreadTest@3d51f06e rejected from java.util.concurrent.ThreadPoolExecutor@7ed7259e[Running, pool size = 4, active threads = 0, queued tasks = 0, completed tasks = 4]

It can be seen that when the task queue is synchronous queue, there is no capacity, the submitted task will be executed immediately, and the current thread will be blocked after execution and will not be reused. Therefore, when the number of threads created is greater than maximumPoolSize, the rejection policy is directly executed and an exception is thrown.
Generally, when using SynchronousQueue, it is required to be unbounded (maximumPoolSize=Integer.MAX_VALUE).
2. Bounded task queue: it is generally implemented by ArrayBlockingQueue

ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(3),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Using the ArrayBlockingQueue bounded task queue, if a new task needs to be executed, the thread pool will create a new thread. Until the number of threads created reaches corePoolSize, the new task will be added to the waiting queue. Here, the number of tasks in the waiting queue is set to 3. If the waiting queue is full, that is, it exceeds the capacity initialized by ArrayBlockingQueue, continue to create threads until the number of threads reaches the maximum number set by maximumPoolSize. If it is greater than maximumPoolSize, execute the rejection policy.
3. Unbounded task queue: it is generally implemented by LinkedBlockingQueue

ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

Using unbounded task queue, the task queue of thread pool can add new tasks without limit, and the maximum number of threads created by thread pool is the number set by corePoolSize. In this case, the value set by maximumPoolSize will not take effect, even if there are many unexecuted tasks in the waiting queue, When the number of thread pools reaches the value of corePoolSize, no new threads will be added.
4. Priority task queue: it is generally implemented by PriorityBlockingQueue

    public static class ThreadTest implements Runnable,Comparable<ThreadTest>{
        private int num;
        ThreadTest(int i){
            this.num = i;
        }
        //order
        //public int compareTo(ThreadTest o) {
        //    return  this.num-o.num;
        //}
        // Reverse order
        public int compareTo(ThreadTest o) {
            return  this.num>o.num?-1:1;
        }
        @Override
        public void run() {
            try{
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"task"+num);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
         ExecutorService pool = new ThreadPoolExecutor(1,3, 1000, TimeUnit.MILLISECONDS, new PriorityBlockingQueue<Runnable>(),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
         for(int i = 1;i<10;i++){
             pool.execute(new ThreadTest(i));
         }
    }

The operation results are as follows:
pool-1-thread-1 task 1
pool-1-thread-1 task 9
pool-1-thread-1 task 8
pool-1-thread-1 task 7
pool-1-thread-1 task 6
pool-1-thread-1 task 5
pool-1-thread-1 task 4
pool-1-thread-1 task 3
pool-1-thread-1 task 2
Because the corePoolSize is set to 1 and the thread sleeps for 1 second, all tasks except 1 enter the waiting queue, and then rearrange the execution according to the specified priority rule, and the number of threads in the thread pool is always corePoolSize.

Topics: Java Multithreading