8. Executor thread pool principle and source code interpretation of concurrent programming

Thread Pool

"Thread pool", as the name implies, is a thread cache. Threads are scarce resources. If they are created without limit, it will not only consume system resources, but also reduce the stability of the system. Therefore, Java provides a thread pool to uniformly allocate and dispatch threads. Excellent and monitoring

Introduction to thread pool

In web development, the server needs to accept and process requests, so a thread is assigned to a request for processing. It is very simple to implement if a new thread is created for each request, but there is a problem:
if the number of concurrent requests is very large, but the execution time of each thread is very short, threads will be frequently created and destroyed. It will greatly reduce the efficiency of the system. It may happen that the server spends more time and consumes more system resources creating new threads and destroying threads for each request than processing actual user requests.
So is there a way to execute a task without being destroyed, but to continue to execute other tasks?
This is the purpose of the thread pool. Thread pools provide a solution to thread lifetime overhead and resource starvation problems. By reusing threads for multiple tasks, the overhead of thread creation is amortized across multiple tasks.

When to use thread pool?

  • Single task processing time is relatively short
  • The number of tasks to be processed is large

Advantages of using thread pool:

  • Reuse existing threads, reduce the overhead of thread creation and extinction, and improve performance
  • Improve responsiveness. When a task arrives, the task can be executed immediately without waiting for the thread to be created.
  • Improve thread manageability. Threads are scarce resources. If created without limit, it will not only consume system resources, but also reduce the stability of the system. Using thread pool can be used for unified allocation, tuning and monitoring.

Implementation of threads

Runnable,Thread,Callable

// Runnable Thread
public interface Runnable {
    
    
    // run
    public abstract void run();
}
public interface Callable<V> {
    
    
	// 相当于run方法有返回值的call方法
    V call() throws Exception;
}

Executor framework

The Executor interface is the most basic part of the thread pool framework, which defines an execute method for executing Runnable
.
The following figure shows its inheritance and implementation:
insert image description here
From the figure, it can be seen that there is an important sub-interface ExecutorService under Executor, which defines the specific behavior of the thread pool
1, execute(Runnable command): perform the task of the Ruannable type,
2, submit( task): can be used to submit Callable or Runnable tasks, and return the Future object representing this task
3, shutdown(): close the service after completing the submitted tasks, and no longer accept new tasks,
4, shutdownNow(): stop all ongoing tasks Execute the task and close the service.
5. isTerminated(): Test whether all tasks have been fulfilled.
6. isShutdown(): Test whether the ExecutorService has been shut down.

Thread pool key attributes

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));
private static final int COUNT_BITS = Integer.SIZE ­ 3;
private static final int CAPACITY   = (1 << COUNT_BITS) ­ 1;

ctl is a field that controls the running status of the thread pool and the number of valid threads in the thread pool. It contains two parts of information: the running status of the thread pool (runState) and the number of valid threads in the thread pool (workerCount). Here you can See, the Integer type is used to save, the upper 3 bits save the runState, and the lower 29 bits save the workerCount. COUNT_BITS is 29, and CAPACITY is 1 left shifted by 29 bits minus 1 (29 1s). This constant represents the upper limit of workerCount, which is about 500 million.

ctl related methods

private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }
private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

runStateOf: Get the running state;
workerCountOf: Get the number of active threads;
ctlOf: Get the value of the running state and the number of active threads.

There are five states in the thread pool
insert image description here
1. RUNNING
(1) State description: When the thread pool is in the RUNNING state, it can receive new tasks and process added tasks.
(2) State switching: The initialization state of the thread pool is RUNNING. In other words, once the thread pool is created, it is in the RUNNING state, and the number of tasks in the thread pool is 0

2. SHUTDOWN
(1) State description: When the thread pool is in the SHUTDOWN state, it does not receive new tasks, but can process added tasks.
(2) State switching: When the shutdown() interface of the thread pool is called, the thread pool changes from RUNNING -> SHUTDOWN.

3. STOP
(1) State Description: When the thread pool is in the STOP state, it does not receive new tasks, does not process added tasks, and interrupts the tasks being processed.
(2) State switching: When the shutdownNow() interface of the thread pool is called, the thread pool is changed from (RUNNING or SHUTDOWN ) -> STOP.

4. TIDYING
(1) State description: When all tasks have been terminated and the "number of tasks" recorded by ctl is 0, the thread pool will change to TIDYING state. When the thread pool becomes TIDYING state, the hook function terminated() will be executed. terminated() is empty in the ThreadPoolExecutor class, if the user wants to perform corresponding processing when the thread pool becomes TIDYING; it can be realized by overloading the terminated() function.
(2) State switching: When the thread pool is in the SHUTDOWN state, the blocking queue is empty and the tasks executed in the thread pool are also empty, it will be SHUTDOWN -> TIDYING. When the thread pool is in the STOP state and the tasks executed in the thread pool are empty, it will be STOP -> TIDYING.

5. TERMINATED
(1) State Description: When the thread pool is completely terminated, it becomes TERMINATED state.
(2) State switching: When the thread pool is in the TIDYING state, after executing terminated(), it will be TIDYING -> TERMINATED.
The conditions for entering TERMINATED are as follows:

  • The thread pool is not in RUNNING state;
  • The thread pool state is not TIDYING state or TERMINATED state;
  • If thread pool status is SHUTDOWN and workerQueue is empty;
  • workerCount is 0;
  • Set TIDYING status successfully.
    insert image description here

ThreadPoolExecutor

Creation of thread pool

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

Task submission
1. public void execute() //No return value for submitting a task
2. public Future<?> submit() //There is a return value after task execution is completed

Parameters explain the number of core threads in
the corePoolSize thread pool. When a task is submitted, the thread pool creates a new thread to execute the task until the current number of threads is equal to corePoolSize; if the current number of threads is corePoolSize, the tasks that continue to be submitted are saved in the blocking queue , waiting to be executed; if the prestartAllCoreThreads() method of the thread pool is executed, the thread pool will create and start all core threads in advance.

maximumPoolSize The maximum number of threads allowed in the thread pool. If the current blocking queue is full and the task continues to be submitted, a new thread is created to perform the task, provided that the current number of threads is less than the maximumPoolSize;

keepAliveTime The idle time allowed by the thread pool maintenance thread. When the number of threads in the thread pool is greater than corePoolSize, if there is no new task submission at this time, the threads outside the core thread will not be destroyed immediately, but will wait until the waiting time exceeds keepAliveTime;

workQueue
is used to save the blocking queue of tasks waiting to be executed, and the task must implement the Runable interface. The following blocking queues are provided in JDK :
1. ArrayBlockingQueue: a bounded blocking queue based on an array structure, sorting tasks by FIFO;
2. LinkedBlockingQuene : Blocking queue based on linked list structure, sorting tasks by FIFO, throughput
is usually higher than ArrayBlockingQuene;
3, SynchronousQuene: a blocking queue that does not store elements, each insertion operation must wait until another thread calls the removal operation, otherwise insert The operation is always in a blocked state, and the throughput is usually higher than that
of LinkedBlockingQuene; 4. priorityBlockingQuene: an unbounded blocking queue with priority; threadFactory
is a variable of type ThreadFactory used to create new threads. Executors.defaultThreadFactory() is used by default to create threads. When using the default ThreadFactory to create a thread, the newly created thread has the same NORM_PRIORITY priority and is a non-daemon thread, and the thread name is also set.

The saturation strategy of the handler thread pool. When the blocking queue is full and there are no idle worker threads, if the task continues to be submitted, a
strategy must be adopted to process the task. The thread pool provides 4 strategies:
1. AbortPolicy: Throw directly Exception, default policy;
2. CallerRunsPolicy: Use the thread where the caller is to execute the task;
3. DiscardOldestPolicy: Discard the frontmost task in the blocking queue and execute the current task;
4. DiscardPolicy: Discard the task directly;
the above 4 policies Both are internal classes of ThreadPoolExecutor.
Of course, you can also implement the RejectedExecutionHandler interface according to the application scenario, and customize the saturation strategy, such as logging or persistently storing tasks that cannot be processed.

Thread pool monitoring
public long getTaskCount() //The total number of executed and unexecuted tasks in the thread pool public long getCompletedTaskCount() //The number of completed tasks public int getPoolSize() //The current number of threads in the thread pool public int getActiveCount(
) //The number of threads executing tasks in the thread pool

Principle of thread pool
insert image description here

Source code analysis:

execute方法
public void execute(Runnable command) {
    
    
    if (command == null)
        throw new NullPointerException();
   /*
* clt记录着runState和workerCount
    */
    int c = ctl.get();
/*
* workerCountOf方法取出低29位的值,表示当前活动的线程数;
* 如果当前活动线程数小于corePoolSize,则新建一个线程放入线程池中; * 并把任务添加到该线程中。
*/
    if (workerCountOf(c) < corePoolSize) {
    
    
       /*
* addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize 来判断还是maximumPoolSize来判断;
* 如果为true,根据corePoolSize来判断;
* 如果为false,则根据maximumPoolSize来判断 */
if (addWorker(command, true))
            return;
/*
* 如果添加失败,则重新获取ctl值 */
        c = ctl.get();
    }
/*
* 如果当前线程池是运行状态并且任务添加到队列成功 */
if (isRunning(c) && workQueue.offer(command)) {
    
     // 重新获取ctl值
int recheck = ctl.get();
// 再次判断线程池的运行状态,如果不是运行状态,由于之前已经把command
添加到workQueue中了,
// 这时需要移除该command
// 执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回 if (! isRunning(recheck) && remove(command))
            reject(command);
       /*
* 获取线程池中的有效线程数,如果数量是0,则执行addWorker方法
* 这里传入的参数表示:
* 1. 第一个参数为null,表示在线程池中创建一个线程,但不去启动;
* 2. 第二个参数为false,将线程池的有限线程数量的上限设置为
maximumPoolSize,添加线程时根据maximumPoolSize来判断;
* 如果判断workerCount大于0,则直接返回,在workQueue中新增的
command会在将来的某个时刻被执行。 */
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
} /*
* 如果执行到这里,有两种情况:
* 1. 线程池已经不是RUNNING状态;
}
* 2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且 workQueue已满。
* 这时,再次调用addWorker方法,但第二个参数传入为false,将线程池的有限 线程数量的上限设置为maximumPoolSize;
* 如果失败则拒绝该任务
 */
 else if (!addWorker(command, false))
     reject(command);
 }

To put it simply, if the state is always RUNNING when executing the execute() method, the execution process is as follows:

  1. If workerCount < corePoolSize, create and start a thread to execute the newly submitted task;
  2. If workerCount >= corePoolSize, and the blocking queue in the thread pool is not full, add the task to the blocking queue;
  3. If workerCount >= corePoolSize && workerCount < maximumPoolSize, and the blocking queue in the thread pool is full, create and start a thread to execute the newly submitted task;
  4. If workerCount >= maximumPoolSize, and the blocking queue in the thread pool is full, the task will be processed according to the rejection strategy, and the default processing method is to throw an exception directly.
    Here we should pay attention to addWorker(null, false);, which is to create a thread, but no task is passed in, because the task has been added to the workQueue, so when the worker executes, it will directly obtain the task from the workQueue. Therefore, execute addWorker(null, false) when workerCountOf(recheck) == 0; also to ensure that the thread pool must have a thread to perform tasks in the RUNNING state

The execution process of the execute method is as follows:
insert image description here
addWorker method
The main work of the addWorker method is to create a new thread in the thread pool and execute it. The firstTask parameter is used to specify the first task to be executed by the newly added thread. When threading, it will judge whether the number of current active threads is less than corePoolSize. false means that it needs to judge whether the number of current active threads is less than maximumPoolSize before adding new threads. The code is as follows:

private boolean addWorker(Runnable firstTask, boolean core) {
    
    
    retry:
    for (;;) {
    
    
        int c = ctl.get();
// 获取运行状态
int rs = runStateOf(c);
/*
* 这个if判断
* 如果rs >= SHUTDOWN,则表示此时不再接收新任务;
* 接着判断以下3个条件,只要有1个不满足,则返回false:
* 1. rs == SHUTDOWN,这时表示关闭状态,不再接受新提交的任务,但却 可以继续处理阻塞队列中已保存的任务
* 2. firsTask为空
* 3. 阻塞队列不为空 *
* 首先考虑rs == SHUTDOWN的情况
* 这种情况下不会接受新提交的任务,所以在firstTask不为空的时候会返回
false;
* 然后,如果firstTask为空,并且workQueue也为空,则返回false, * 因为队列中已
*/
        // Check if queue empty only if necessary.
     if (rs >= SHUTDOWN &&
             ! (rs == SHUTDOWN &&
                     firstTask == null &&
                     ! workQueue.isEmpty()))
         return false;
for (;;) {
    
    
// 获取线程数
int wc = workerCountOf(c);
// 如果wc超过CAPACITY,也就是ctl的低29位的最大值(二进制是29个1),返回false;
// 这里的core是addWorker方法的第二个参数,如果为true表示根据corePoolSize来比较,
// 如果为false则根据maximumPoolSize来比较。
            //
         if (wc >= CAPACITY ||
                 wc >= (core ? corePoolSize : maximumPoolSize))
             return false;
// 尝试增加workerCount,如果成功,则跳出第一个for循环 if (compareAndIncrementWorkerCount(c))
break retry;
// 如果增加workerCount失败,则重新获取ctl的值
c = ctl.get();  // Re­read ctl
// 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个 for循环继续执行
            if (runStateOf(c) != rs)
                continue retry;
               // else CAS failed due to workerCount change; retry
inner loop }
    }
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
    
    
// 根据firstTask来创建Worker对象 w = new Worker(firstTask);
// 每一个Worker对象都会创建一个线程 final Thread t = w.thread;
if (t != null) {
    
    
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
    
    
int rs = runStateOf(ctl.get());
// rs < SHUTDOWN表示是RUNNING状态;
// 如果rs是RUNNING状态或者rs是SHUTDOWN状态并且firstTask为null,向线程池中添加线程。
// 因为在SHUTDOWN时不会在添加新的任务,但还是会执行workQueue中的任务startable
if (rs < SHUTDOWN ||
        (rs == SHUTDOWN && firstTask == null)) {
    
    
    if (t.isAlive()) // precheck that t is
throw new IllegalThreadStateException(); // workers是一个HashSet
workers.add(w);
int s = workers.size();
// largestPoolSize记录着线程池中出现过的最大线程数量
if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
    
    
                mainLock.unlock();
            }
            if (workerAdded) {
    
    
// 启动线程
t.start(); workerStarted = true;
} }
    } finally {
    
    
        if (! workerStarted)
            addWorkerFailed(w);
}
    return workerStarted;
}

Worker class
Each thread in the thread pool is encapsulated into a Worker object. What the ThreadPool maintains is actually a set of Worker objects. Please refer to the JDK source code.
The Worker class inherits AQS and implements the Runnable interface. Pay attention to the firstTask and thread attributes: firstTask uses it to save incoming tasks; thread is a thread created by ThreadFactory when calling the constructor, and is used to process tasks thread.
When calling the constructor, you need to pass in the task. Here, getThreadFactory().newThread(this); is used to create a new thread. The parameter passed in by the newThread method is this, because the Worker itself inherits the Runnable interface, which is a thread. So a Worker object will call the run method in the Worker class when it starts.
Worker inherits AQS and uses AQS to realize the function of exclusive lock. Why not use ReentrantLock to achieve it? You can see the tryAcquire method, which does not allow reentrancy, and ReentrantLock allows reentrancy:

  1. Once the lock method acquires the exclusive lock, it means that the current thread is executing the task;
  2. If a task is being executed, the thread should not be interrupted;
  3. If the thread is not in the exclusive lock state, that is, in the idle state, it means that it is not processing tasks, and the thread can be interrupted at this time;
  4. When the thread pool executes the shutdown method or the tryTerminate method, it will call the interruptIdleWorkers method to interrupt idle threads, and the interruptIdleWorkers method will use the tryLock method to determine whether the threads in the thread pool are idle;
  5. The reason it is set to non-reentrant is because we don't want tasks to reacquire locks when calling thread pool control methods like setCorePoolSize. If ReentrantLock is used, it is reentrant, so that if a thread pool control method such as setCorePoolSize is called in a task, it will interrupt the running thread.

Therefore, Worker inherits from AQS and is used to determine whether a thread is idle and whether it can be interrupted.
In addition, setState(-1); is executed in the construction method, and the state variable is set to -1. Why do you do this? It is because the default state in AQS is 0. If a Worker object has just been created, the task has not yet been executed At this time,
it should not be interrupted at this time, look at the tryAquire method:

protected boolean tryAcquire(int unused) {
    
     //cas修改state,不可重入
    if (compareAndSetState(0, 1)) {
    
    
        setExclusiveOwnerThread(Thread.currentThread());
        return true;
}
    return false;
}

The tryAcquire method is judged based on whether the state is 0, so setState(-1); setting the state to -1 is to prohibit the thread from being interrupted before executing the task.
Because of this, in the runWorker method, the unlock method of the Worker object will be called first to set the state to 0.
runWorker method
The run method in the Worker class calls the runWorker method to perform tasks. The code of the runWorker method is as follows:

    final void runWorker(Worker w) {
    
    
        Thread wt = Thread.currentThread();
        // 获取第一个任务
        Runnable task = w.firstTask;
        w.firstTask = null;
        // 允许中断
        w.unlock(); // allow interrupts
        // 是否因为异常退出循环
        boolean completedAbruptly = true;
        try {
    
    
            // 如果task为空,那么使用getTask获取任务
            while (task != null || (task = getTask()) != null) {
    
    
                w.lock();
                if ((runStateAtLeast(ctl.get(), STOP) ||
                        (Thread.interrupted() &&
                                runStateAtLeast(ctl.get(), STOP))) &&
                        !wt.isInterrupted())
                    wt.interrupt();
                try {
    
    
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
    
    
                        task.run();
                    } catch (RuntimeException x) {
    
    
                        thrown = x;
                        throw x;
                    } catch (Error x) {
    
    
                        thrown = x;
                        throw x;
                    } catch (Throwable x) {
    
    
                        thrown = x;
                        throw new Error(x);
                    } finally {
    
    
                        afterExecute(task, thrown);
                    }
                } finally {
    
    
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
    
    
            processWorkerExit(w, completedAbruptly);
        }
    }

Here is an explanation of the first if judgment, the purpose is: if the thread pool is stopping, then ensure that the current thread is in an interrupted state; if not, ensure that the current thread is not in an interrupted state;

Here we should consider that the shutdownNow method may also be executed during the execution of the if statement. The shutdownNow method will set the state to STOP. Review the STOP state: it cannot
accept new tasks, nor process tasks in the queue, and it will interrupt the thread that is processing the task . When the thread pool is in the RUNNING or SHUTDOWN state, calling the shutdownNow() method causes the thread pool to enter that state.

The STOP state interrupts all threads in the thread pool, and Thread.interrupted() is used here to determine whether the interruption is to ensure that the thread is in a non-interrupted state in the RUNNING or SHUTDOWN state, because the Thread.interrupted() method will reset the interrupted state .

Summarize the execution process of the runWorker method:

  1. The while loop continuously obtains tasks through the getTask() method;
  2. The getTask() method fetches tasks from the blocking queue;
  3. If the thread pool is stopping, then ensure that the current thread is in an interrupted state, otherwise, ensure that the current thread is not in an interrupted state;
  4. Call task.run() to execute the task;
  5. If the task is null, jump out of the loop and execute the processWorkerExit() method;
  6. The execution of the runWorker method also means that the execution of the run method in the Worker is completed and the thread is destroyed.
    The beforeExecute method and afterExecute method here are empty in the ThreadPoolExecutor class and are left to subclasses to implement.
    The completedAbruptly variable indicates whether an exception occurs during task execution, and the value of this variable will be judged in the processWorkerExit method.

getTask method
The getTask method is used to fetch tasks from the blocking queue, the code is as follows:

private Runnable getTask() {
    
    
	//轮询是否超时的标识
	boolean timedOut = false;
	//自旋for循环
	for (;;) {
    
    
		//获取ctl
		int c = ctl.get();
		//获取线程池的状态
		int rs = runStateOf(c);
 
		//检测任务队列是否在线程池停止或关闭的时候为空
		//也就是说任务队列是否在线程池未正常运行时为空
		if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
    
    
			//减少Worker线程的数量
			decrementWorkerCount();
			return null;
		}
		//获取线程池中线程的数量
		int wc = workerCountOf(c);
 
		//检测当前线程池中的线程数量是否大于corePoolSize的值或者是否正在等待执行任务
		boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
 
		//如果线程池中的线程数量大于corePoolSize
		//获取大于corePoolSize或者是否正在等待执行任务并且轮询超时
		//并且当前线程池中的线程数量大于1或者任务队列为空
		if ((wc > maximumPoolSize || (timed && timedOut))
			&& (wc > 1 || workQueue.isEmpty())) {
    
    
			//成功减少线程池中的工作线程数量
			if (compareAndDecrementWorkerCount(c))
				return null;
			continue;
		}
 
		try {
    
    
			//从任务队列中获取任务
			Runnable r = timed ?
				workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
				workQueue.take();
			//任务不为空直接返回任务
			if (r != null)
				return r;
			timedOut = true;
		} catch (InterruptedException retry) {
    
    
			timedOut = false;
		}
	}
}

The important point here is the second if judgment, the purpose is to control the effective number of threads in the thread pool. From the above analysis, we can know that when executing the execute method, if the number of threads in the current thread pool exceeds corePoolSize and is less than maximumPoolSize, and the workQueue is full, you can increase the worker thread, but if the timeout does not get the task, That is, when timedOut is true, it means that the workQueue is empty, which means that there are no more threads in the current thread pool to perform tasks. You can destroy more threads than corePoolSize, and keep the number of threads at corePoolSize. Can.

When will it be destroyed? Of course, after the runWorker method is executed, that is, the run method in the Worker is executed, and it will be automatically recycled by the JVM.

When the getTask method returns null, the while loop will jump out of the runWorker method, and then the processWorkerExit method will be executed.

processWorkerExit method

private void processWorkerExit(Worker w, boolean completedAbruptly) {
    
    
         // 因为抛出用户异常导致线程终结,直接使工作线程数减1即可
         // 如果没有任何异常抛出的情况下是通过getTask()返回null引导线程 

         //正常跳出runWorker()方法的while死循环从而正常终结,这种情况下,在getTask()中已经把线程数减1
         if (completedAbruptly)// If abrupt, then workerCount wasn't adjusted

                  decrementWorkerCount();

         final ReentrantLock mainLock = this.mainLock;
         mainLock.lock();
         try {
    
    
              // 全局的已完成任务记录数加上此将要终结的Worker中的已完成任务数
              completedTaskCount += w.completedTasks;
              // 工作线程集合中移除此将要终结的Worker
              workers.remove(w);
         } finally {
    
    
             mainLock.unlock();
         }
    
        // 见下一小节分析,用于根据当前线程池的状态判断是否需要进行线程池terminate处理
        tryTerminate();

        int c = ctl.get();
        // 如果线程池的状态小于STOP,也就是处于RUNNING或者SHUTDOWN状态的前提下:
        // 1.如果线程不是由于抛出用户异常终结,如果允许核心线程超时,则保持线程池中至少存在一个工作线程
        // 2.如果线程由于抛出用户异常终结,或者当前工作线程数,那么直接添加一个新的非核心线程
        if (runStateLessThan(c, STOP)) {
    
    
             if (!completedAbruptly) {
    
    
                // 如果允许核心线程超时,最小值为0,否则为corePoolSize
               int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
               // 如果最小值为0,同时任务队列不空,则更新最小值为1
               if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                    // 工作线程数大于等于最小值,直接返回不新增非核心线程
                   if (workerCountOf(c) >= min)
                     return; // replacement not needed
         }
         addWorker(null, false);
       }
  }

So far, after processWorkerExit is executed, the worker thread is destroyed. The above is the life cycle of the entire worker thread. Starting from the execute method, the Worker uses ThreadFactory to create a new worker thread. RunWorker obtains the task through getTask, and then executes the task. If getTask returns null, Enter the processWorkerExit method, and the entire thread ends, as shown in the figure:
insert image description here
Summary

  • Analyzed thread creation, task submission, state transition and thread pool shutdown;
  • Here, the execute method is used to expand the workflow of the thread pool. The execute method uses the corePoolSize, maximumPoolSize and the size of the blocking queue to determine whether the incoming task should be executed immediately, added to the blocking queue, or rejected.
  • Introduces the process when the thread pool is closed, and also analyzes the race condition between the shutdown method and the getTask method;
  • When obtaining a task, the state of the thread pool should be used to judge whether the worker thread should be terminated or the blocked thread is waiting for a new task. It also explains why the worker thread is interrupted when the thread pool is closed and why each worker needs a lock.

Guess you like

Origin blog.csdn.net/qq_39513430/article/details/112015277