Java thread pool ThreadPoolExecutor analysis

Java thread pool

Starting from today, I will spend some time every week to write a little technical blog, which can be regarded as a precipitation of my weekly learning. The previous CSDN account was lost, and now the account has been re-registered. This article mainly discusses the Java thread pool. The Alibaba Java Development Manual recommends using ThreadPoolExcutor to build a thread pool, and it is recommended not to create wild threads at will. The so-called wild thread is a multi-threaded class without unified management. Such a multi-threaded class is not conducive to unified management. In actual projects, if the created thread is not effectively controlled, it is likely to cause the thread to fail to close, memory overflow, etc. a series of questions. This article will explain the Java thread pool from the following aspects:

  • The concept of Java thread pool
  • Introduction to ThreadPoolExecutor
  • Related source code analysis
  • Summarize

1. What is a thread pool
With the development of CPU hardware technology, PCs are gradually developing from a single-core era to a multi-core era. The multi-core CPU enables task execution to be processed in parallel, which can greatly improve the efficiency of task execution. When the task arrives, a thread is created to execute the task, and when the task is completed, the thread is closed. When the number of concurrent tasks is large, threads will be created and closed continuously, and these operations actually have a great impact on the consumption of system resources. At the same time, if there are too many threads, these active threads may also consume system resources, because system resources will be occupied by thread creation, thread switching state, and thread destruction.
The so-called thread pool means that the thread is not destroyed immediately after the thread is created and the task is completed, but the thread is put into the thread pool. When the next task arrives, the thread that has been created in the thread pool is used to complete the execution of the task.
The advantages of the thread pool:
a. Unified management of thread resources;
b. The management strategy of the thread pool is clearly indicated, which is convenient for developers to understand;
it is also written in the development manual that it is not allowed to use the thread pool to manage threads. Executors to create, the disadvantages are as follows:
a. The request queue length allowed in FixedThreadPool and SingleThreadPool is Integer.MAX_VALUE, which may accumulate a large number of thread tasks, resulting in memory overflow; b.
CachedThreadPool and ScheduledThreadPool allow the number of created threads to be Integer. MAX_VALUE may create a large number of threads and cause memory overflow.
2. ThreadPoolExecutor introduction

public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
            BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);

    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
        BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

As can be seen from the above code, ThreadPoolExcutor has a total of four constructors, of which the first three constructors are the implementations that call the last constructor.
The meaning of each parameter:
(1) coolPoolSize: the number of core threads.
(2) maximumPoolSize: The thread pool maintains the maximum number of threads.
(3) keepAliveTime: The idle time allowed by the thread pool maintenance thread.
(4) unit: idle time unit.
(5) workQueue: The task buffer queue used by the thread pool.
(6) threadFactory: Thread factory, used to create threads.
(7) handler: The thread pool task rejection policy when the thread task reaches the upper limit of the queue.

The following describes the workflow of the ThreadPoolExecutor thread pool. When a thread task enters the thread pool, it submits a thread task to the thread pool through the execute() method, which is a Runnable object.
After the thread task is submitted to the thread pool, when the number of threads in the current thread pool is less than coolPoolSize, even if the threads in the thread pool are idle, the thread pool will create new threads to execute the tasks submitted to the thread pool.
When the number of threads in the thread pool reaches coolPoolSize and the buffer queue is still free, the newly submitted task will enter the workQueue and wait for execution.
When the number of threads in the thread pool is greater than coolPoolSize, and the task buffer queue is empty and the thread is smaller than maximumPoolSize, the thread pool will create a temporary emergency thread to process the incoming task.
When the number of threads in the thread pool is greater than coolPoolSize, and the task buffer queue is empty and the number of threads is equal to maximumPoolSize, the thread pool will process incoming thread tasks according to the strategy specified by the handler.
3. Related source code analysis

Let's analyze the source code of the executor method in the thread pool

 //原子操作整型类,32位,其中前3位表示线程状态,后29位标识线程的数量
 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

 public void execute(Runnable command) {
        //1.判断执行的线程任务是否空,如果为空抛出空指针异常
        if (command == null)
            throw new NullPointerException();
        //2.获取线程状态以及线程数量
        int c = ctl.get();
        //3.判断当前线程池数量是否小于corePoolSize,小于则创建新线程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //4.如果线程处于运行状态同时线程可以添加到缓冲队列中
        if (isRunning(c) && workQueue.offer(command)) {
        //5.再次检查线程状态(防止上次检查后的线程已销毁或者出现线程池关闭的情况)
            int recheck = ctl.get();
        //6.如果当前线程状态不是运行状态,那么从队列中删除此任务,同时执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
        //7.否则当前线程池为空,则创建一个线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //8.如果队列满了,则新增线程,新增失败则执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

addWorkder method

private boolean addWorker(Runnable firstTask, boolean core) {
        //1.通过cas来增加线程池中的线程个数
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
        //2.并发安全的将任务添加到workers里面,同时启动任务执行
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            //创建worker
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                //加锁,防止其他线程调用
                mainLock.lock();
                try {
                    //重新检查线程状态
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        //添加任务
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                //添加成功则启动任务
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

4. Summary
Through the use of Java thread pool, the reuse of threads is realized, and the system overhead caused by thread creation and destruction is reduced. At the same time, the system overhead caused by thread context switching is avoided.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325382737&siteId=291194637