The end of Java concurrency series: thoroughly understand the working principle of java thread pool and want to enter BTAJ

//Use the thread pool to perform a task

executor.execute(() -> {

// Do something

});

//Closing the thread pool will prevent the submission of new tasks, but it will not affect the submitted tasks

executor.shutdown();

//Close the thread pool, prevent new task submission, and interrupt the currently running thread

executor.showdownNow();



Call directly after creating the thread pool execute Method and pass in a Runnable Parameter to hand over the task to the thread pool for execution shutdown/shutdownNow Method to close the thread pool.



ThreadPoolExecutor There are many parameters in the construction method. For beginners, it is difficult to configure an appropriate thread pool without understanding the role of each parameter. therefore Java It also provides us with a thread pool tool class Executors To quickly create a thread pool. Executors Many simple methods for creating thread pools are provided. For example, the code is as follows:



//Instantiate a single threaded thread pool

ExecutorService singleExecutor = Executors.newSingleThreadExecutor();

//Create a thread pool with a fixed number of threads

ExecutorService fixedExecutor = Executors.newFixedThreadPool(10);

//Create a reusable thread pool with a fixed number of threads

ExecutorService executorService2 = Executors.newCachedThreadPool();



However, generally speaking, direct use is not recommended in actual development Executors To create a thread pool, you need to configure a thread pool suitable for your project according to the actual situation of the project. Later, we need to understand the parameters of the thread pool and the working principle of the thread pool.



2.Thread pool lifecycle

==========



From birth to death, thread pool will experience RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED Five lifecycle states.



*   *   **RUNNING** Indicates that the thread pool is running, can accept newly submitted tasks, and can process added tasks. RUNNING State is the initialization state of the thread pool. Once the thread pool is created, it is in the initialization state RUNNING Status.

    *   **SHUTDOWN** The thread is closed and does not accept new tasks, but can process added tasks. RUNNING Thread pool call with state shutdown Will enter after SHUTDOWN Status.

    *   **STOP** The thread pool is in a stopped state, does not receive tasks, does not process added tasks, and will interrupt the threads executing tasks. RUNNING Thread pool in state called shutdownNow Will enter after STOP Status.

    *   **TIDYING** When all tasks have been terminated and the number of tasks is 0, the thread pool will enter TIDYING. When the thread pool is in SHUTDOWN In status, the tasks in the blocking queue have been executed, and there are no executing tasks in the thread pool. The status will be determined by SHUTDOWN Become TIDYING. When the thread is in STOP When there are no tasks in progress in the thread pool, the STOP Become TIDYING. 

    *   **TERMINATED** Thread termination status. be in TIDYING Thread execution in state terminated()Post entry TERMINATED Status.



According to the above description of thread pool life cycle state, the following thread pool life cycle state flow diagram can be drawn.



1.ThreadPoolExecutor Parameters in

========================



In the previous section, we used ThreadPoolExecutor To create a thread pool. Actually ThreadPoolExecutor There are multiple constructors in, but they all call this constructor in the following code:



public class ThreadPoolExecutor extends AbstractExecutorService {

public ThreadPoolExecutor(int corePoolSize,

                          int maximumPoolSize,

                          long keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue,

                          ThreadFactory threadFactory,

                          RejectedExecutionHandler handler) {

    // ... Omit verification related codes

    

    this.corePoolSize = corePoolSize;

    this.maximumPoolSize = maximumPoolSize;

    this.workQueue = workQueue;

    this.keepAliveTime = unit.toNanos(keepAliveTime);

    this.threadFactory = threadFactory;

    this.handler = handler;

}



// ...    

}



There are seven parameters in this construction method. Let's look at the meaning of each parameter one by one:



*   **corePoolSize** Represents the number of core threads in the thread pool. When a task is submitted to the thread pool, if the number of threads in the thread pool is less than corePoolSize,Then create a new thread to execute the task directly.

*   **workQueue** Task queue, which is a blocking queue used to store tasks that are too late to execute. When a task is submitted to the thread pool, if the number of threads in the thread pool is greater than or equal to corePoolSize,Then the task will be put into the queue and wait for execution.

*   **maximumPoolSize** Indicates the maximum number of threads supported by the thread pool. When a task is submitted to the thread pool, the number of threads in the thread pool is greater than corePoolSize,also workQueue If it is full, a new thread will be created to execute the task, but the number of threads must be less than or equal to maximumPoolSize. 

*   **keepAliveTime** The time that a non core thread remains alive when it is idle. Non core thread workQueue Threads created when a task is submitted after it is full. Because these threads are not core threads, they are idle for more than keepAliveTime It will be recycled.

*   **unit** The unit of time that a non core thread remains alive when idle

*   **threadFactory** The factory for creating threads can uniformly handle the properties of creating threads here

*   **handler** Reject policy when threads in the thread pool reach maximumPoolSize After the number of threads and workQueue When the thread pool is full, the task is submitted to the thread pool, and the corresponding rejection policy is executed



2.Thread pool workflow

=========



Thread pool commit task is from execute Method, we can start from execute Method to analyze the workflow of the thread pool.



(1)When execute Method, if the number of threads in the thread pool is less than corePoolSize,Then a new thread will be created to execute the task regardless of whether there are idle threads in the thread pool.



![](https://img-blog.csdnimg.cn/20210712145222264.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl81NzkwNzAyOA==,size_16,color_FFFFFF,t_70) 



(2)When execute Method when submitting a task, the number of threads in the thread pool has reached corePoolSize,If there is no idle thread at this time, the task will be stored in workQueue Yes.



![](https://img-blog.csdnimg.cn/img_convert/d9779d2b3a8e41a3b76e82169e6ce947.png)



(3)If execute The number of threads in the thread pool has reached when submitting the task corePoolSize,also workQueue If it is full, a new thread will be created to execute the task, but the number of bus processes should be less than maximumPoolSize.  



(4)If a thread in the thread pool has finished executing the current task, it attempts to start from workQueue Take the first task out of the list to execute. If workQueue Null blocks the thread.



![](https://img-blog.csdnimg.cn/img_convert/1c2cddba4b922b4a301dac3873937d94.png)



(5)If execute When submitting a task, the number of threads in the thread pool reached maximumPoolSize,And workQueue When the is full, a reject policy is executed to reject the task.



![](https://img-blog.csdnimg.cn/img_convert/ccf4135834efe64536ecd31ff82e7b31.png)



(6)If the number of threads in the thread pool exceeds corePoolSize,Then idle time exceeds keepAliveTime The threads will be destroyed, but the number of threads in the process pool will remain at corePoolSize. 



![](https://img-blog.csdnimg.cn/img_convert/8ea28c544aa0ae9fa5ff3688f1fbc475.png)



(7)If there are idle threads in the thread pool and the allowCoreThreadTimeOut by true. Then idle time exceeds keepAliveTime All threads will be destroyed.



![](https://img-blog.csdnimg.cn/img_convert/238d66a64ef2536c0c2d56061ab88748.png)



  

3.Reject policy for thread pool



If the number of threads in the thread pool reaches maximumPoolSize,also workQueue When the queue is full, the thread pool will execute the corresponding rejection policy. stay JDK Provided in RejectedExecutionHandler Interface to perform the reject operation. realization RejectedExecutionHandler There are four classes corresponding to four rejection policies. They are as follows:



*   **DiscardPolicy** When a task submitted to the thread pool is rejected, the thread pool will discard the rejected task

*   **DiscardOldestPolicy** When a task submitted to the thread pool is rejected, the thread pool discards the oldest task in the waiting queue.

*   **CallerRunsPolicy** When a task submitted to the thread pool is rejected, the currently running thread pool will be Thread Process rejected tasks in the thread. That is, which thread submits the task and which thread executes it.

*   **AbortPolicy** Thrown directly when the task submitted to the thread pool is rejected RejectedExecutionException Abnormal.



3, Thread pool source code analysis

=========



From the interpretation of the workflow of thread pool in the previous chapter, it seems that the principle of thread pool is not very difficult. However, I said at the beginning that it is not difficult to understand the source code of thread pool. The main reason is that a lot of concurrency related knowledge is used in thread pool. In addition, it is also related to bit operations used in thread pool.



1.Bit operation in thread pool (learn more)

================



When submitting a task to the thread pool, there are two important parameters that determine the destination of the task. These two parameters are the status of the thread pool and the number of threads in the thread pool. stay ThreadPoolExecutor One is used internally AtomicInteger Integer of type ctl To represent these two parameters, the code is as follows:



public class ThreadPoolExecutor extends AbstractExecutorService {

// Integer.SIZE = 32. So COUNT_BITS= 29

private static final int COUNT_BITS = Integer.SIZE - 3;

// 000011111 11111111111111111111111111111 this value can represent the maximum thread capacity of the thread pool

private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

// Shift - 1 left by 29 bits to get the value of RUNNING status

private static final int RUNNING    = -1 << COUNT_BITS;    

// Thread pool running status and number of threads

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));



private static int ctlOf(int rs, int wc) { return rs | wc; }



// ...

}



Because multithreaded operations are involved, in order to ensure atomicity, ctl Parameter used AtomicInteger Type, and through ctlOf Method to calculate ctl Initial value of. If you don't understand bit operations, it's probably difficult to understand the purpose of the above code.



We know, int Type in Java Occupied 4 byte Memory,One byte Occupancy 8 bit,therefore Java Medium int Type: 32 in total bit. For this 32 bit,We can split the high and low positions. do Android Development students should understand View In the measurement process MeasureSpec Parameter, this parameter will be 32 bit of int Split into high 2 bits and low 30 bits, respectively View Measurement mode and measured value. And here ctl And MeasureSpec similar, ctl Convert 32-bit int It is divided into high 3 bits and low 29 bits, which respectively represent the running state of the thread pool and the number of threads in the thread pool.



Let's verify it by bit operation ctl Of course, if you don't understand the process of bit operation, it has little impact on understanding the source code of thread pool, so students who are not interested in the following verification can skip it directly.



You can see in the above code RUNNING The value of is-1 Shift 29 bits left, we know in the computer\*\*Negative numbers are represented by the complement of their absolute values, and the complement is obtained by adding 1 to the inverse code.\*\*therefore-1 Store the inverse code of form 1 in the computer+1



Original code of 1: 00000000 00000000 00000000 00000001

                                        +

Inverse code of 1: 11111111111111111111111111111111110

   ---------------------------------------

-1 storage: 11111111111111111111111111111111111111



Next, yes-1 Shift 29 bits left to get RUNNING The value of is:



//The top three bits indicate the thread status, that is, the top three bits are 111, indicating RUNNING

11100000 00000000 00000000 00000000



and AtomicInteger The initial number of threads is 0, so ctlOf In method“|"The operation is as follows:



RUNNING: 11100000 00000000 00000000 00000000

                                           |

Number of threads is 0: 00000000 00000000 00000000 00000000

      ---------------------------------------

Get ctl: 11100000 00000000 00000000 00000000



adopt RUNNING|0(Number of threads)Can get ctl Initial value of. At the same time, the following methods can be used to ctl Disassembly into running state and number of threads:



// 00001111 11111111 11111111 11111111

private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

// Get thread pool running status

private static int runStateOf(int c)     { return c & ~COUNT_MASK; }

// Gets the number of threads in the thread pool

private static int workerCountOf(int c)  { return c & COUNT_MASK; }


Assume that the thread pool is RUNNING Status, and the number of threads is 0. Verify it runStateOf How to get the running status of the thread pool:



COUNT_MASK: 00001111 11111111 11111111 11111111

~COUNT_MASK: 11110000 00000000 00000000 00000000

                                               &

ctl: 11100000 00000000 00000000 00000000

         ----------------------------------------

RUNNING: 11100000 00000000 00000000 00000000

Copy code



If you don't understand the above verification process, it doesn't matter, as long as you know it passes runStateOf Method to get the running state of the thread pool workerCountOf You can get the number of threads in the thread pool.



Next, we enter the source code analysis of the source code of the thread pool.



2.ThreadPoolExecutor of execute

============================



The method of submitting tasks to the thread pool is execute method, execute The method is ThreadPoolExecutor And take this method as the entrance to analyze, execute The code of the method is as follows:



public void execute(Runnable command) {

    if (command == null)

        throw new NullPointerException();

    // Get the value of ctl

    int c = ctl.get();

    // 1. The number of threads is less than corePoolSize

    if (workerCountOf(c) < corePoolSize) {

        // If the number of threads in the thread pool is less than the number of core threads, try to create a core thread to execute the task

        if (addWorker(command, true))

            return;

        c = ctl.get();

    }

    // 2. This indicates that the number of threads in the thread pool is greater than the number of core threads or thread creation failed

    if (isRunning(c) && workQueue.offer(command)) {

        // If the thread is running and the task can be added to the blocking queue with offer, offer is a non blocking operation.

        int recheck = ctl.get();

        // Recheck the thread pool status because the thread pool status may change after the last detection. If it is not running, remove the task and execute the reject policy

        if (! isRunning(recheck) && remove(command))

            reject(command);

        // If it is running and the number of threads is 0, a thread is created

        else if (workerCountOf(recheck) == 0)

            // If the number of threads is 0, a non core thread will be created without specifying the task to be executed for the first time. The second parameter here actually has no practical significance

            addWorker(null, false);

    }

    // 3. The blocking queue is full. Create a non core thread to execute the task

    else if (!addWorker(command, false))

        // If it fails, the reject policy is executed

        reject(command);

}


execute The logic in the method can be divided into three parts:



*   1.If the number of threads in the thread pool is less than the core thread, it is called directly addWorker Method to create a new thread to perform the task.

*   2.If the number of threads in the thread pool is greater than the number of core threads, add the task to the blocking queue, and then verify the running state of the thread pool again, because the thread pool state may have changed since the last detection. If the thread pool is closed, remove the task and execute the reject policy. If the thread is still running, but there are no threads in the thread pool, call addWorker Method to create a thread. Note that the task parameter passed in at this time is null,That is, the execution task is not specified because the task has been added to the blocking queue. After creating the thread, take out the task execution from the blocking queue.

*   3.If step 2 fails to add a task to the blocking queue, it indicates that the blocking Queue task is full. Then step 3 will be executed, that is, create a non core thread to execute the task. If the creation of a non core thread fails, the reject policy will be executed.



As you can see, the execution logic of the code is the same as the workflow of the thread pool we analyzed in Chapter 2.



Next look execute Method to create a thread addWoker,addWoker Method undertakes the creation of core threads and non core threads boolean parameter core To distinguish whether to create core threads or non core threads. Let's see first addWorker Code for the first half of the method:



//The return value indicates whether the thread was successfully created

private boolean addWorker(Runnable firstTask, boolean core) {

    // Here is a retry flag, which is equivalent to goto

    retry:

    for (int c = ctl.get();;) {

        // Check if queue empty only if necessary.

        if (runStateAtLeast(c, SHUTDOWN)

            && (runStateAtLeast(c, STOP)

                || firstTask != null

                || workQueue.isEmpty()))

            return false;



        for (;;) {

            // The maximum number of threads created is determined according to the core. If the maximum value is exceeded, thread creation fails. Note that the maximum values here may include s three corePoolSize, maximumPoolSize and the maximum capacity of thread pool threads

            if (workerCountOf(c)

                >= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))

                return false;

            // Use CAS to increase the number of threads by 1. If successful, jump out of the loop and execute the following logic    

            if (compareAndIncrementWorkerCount(c))

                break retry;

            c = ctl.get();  // Re-read ctl

            // The state of the thread pool has changed, and it is returned to retry for re execution

            if (runStateAtLeast(c, SHUTDOWN))

                continue retry;

        }

    }

    

    // ... Omit the second half

   

    return workerStarted;

}


This part of the code will determine the value of the number of threads in the thread pool by whether to create a core thread. If you create a core thread, the maximum value cannot exceed corePoolSize,If you are creating a non core thread, the number of threads cannot exceed maximumPoolSize,In addition, whether creating core threads or non core threads, the maximum number of threads cannot exceed the maximum number of threads allowed by the thread pool COUNT\_MASK(Possible settings maximumPoolSize greater than COUNT\_MASK). Returns if the number of threads is greater than the maximum false,Failed to create thread.



Next pass CAS Increase the number of threads by 1. If successful, then break retry End the infinite loop if CAS If you fail, you will die continue retry From the beginning for Loop, notice here retry no Java Keyword is a character that can be named arbitrarily.



Next, if you can continue to execute downward, you will start to create threads and execute tasks. Take a look addWorker Code for the second half of the method:



private boolean addWorker(Runnable firstTask, boolean core) {

    // ... Omit the first half



    boolean workerStarted = false;

    boolean workerAdded = false;

    Worker w = null;

    try {

        // Instantiate a Worker and encapsulate the thread internally

        w = new Worker(firstTask);

        // Remove the new thread

        final Thread t = w.thread;

        if (t != null) {

            // ReentranLock is used here to ensure thread safety

            final ReentrantLock mainLock = this.mainLock;

            mainLock.lock();

            try {

                int c = ctl.get();

                // Get the lock lake and recheck the thread pool state. The thread will be created only when it is in RUNNING state or SHUTDOWN and firstTask==null

                if (isRunning(c) ||

                    (runStateLessThan(c, STOP) && firstTask == null)) {

                    // If the thread is not in the NEW state, it indicates that the thread has been started and an exception is thrown

                    if (t.getState() != Thread.State.NEW)

                        throw new IllegalThreadStateException();

                    // Add the thread to the thread queue. The worker here is a HashSet   

                    workers.add(w);

                    workerAdded = true;

                    int s = workers.size();

                    if (s > largestPoolSize)

                        largestPoolSize = s;

                }

            } finally {

                mainLock.unlock();

            }

            if (workerAdded) {

                // Start thread to execute task

                t.start();

                workerStarted = true;

            }

        }

    } finally {

        if (! workerStarted)

            addWorkerFailed(w);

    }

    return workerStarted;

}


In fact, this part of logic is easier to understand, that is, creating Worker And start the process of thread execution, Worker It is created by encapsulating threads worker Will be added to ThreadPoolExecutor Medium HashSet Yes. That is, the threads in the thread pool are maintained in this name workers of HashSet In and by ThreadPoolExecutor Managed by, HashSet Threads in may be working or idle. Once the specified idle time is reached, threads will be recycled according to conditions.



We know that thread calls start After execution, the logical code of the thread will be executed. After execution, the life cycle of the thread will end. How can the thread pool be guaranteed Worker Still not finished after completing the task? When the thread is idle and timeout or the thread pool is closed, how to recycle the thread? The implementation logic is actually Worker Yes. look down Worker Code of:



private final class Worker

    extends AbstractQueuedSynchronizer

summary

This article goes from basic to advanced and then to actual combat. It makes MySQL clear and clear. This should be the best learning note about MySQL I have seen so far. I believe that if you carefully read this note, you can easily solve the problems encountered at work or asked by the interviewer!

Important: if you need to get the full version of MySQL learning notes, please forward it + pay attention Click here for free To free download!

Sorting of 50 high-frequency interview questions on MySQL:

}



In fact, this part of logic is easier to understand, that is, creating Worker And start the process of thread execution, Worker It is created by encapsulating threads worker Will be added to ThreadPoolExecutor Medium HashSet Yes. That is, the threads in the thread pool are maintained in this name workers of HashSet In and by ThreadPoolExecutor Managed by, HashSet Threads in may be working or idle. Once the specified idle time is reached, threads will be recycled according to conditions.



We know that thread calls start After execution, the logical code of the thread will be executed. After execution, the life cycle of the thread will end. How can the thread pool be guaranteed Worker Still not finished after completing the task? When the thread is idle and timeout or the thread pool is closed, how to recycle the thread? The implementation logic is actually Worker Yes. look down Worker Code of:



private final class Worker

    extends AbstractQueuedSynchronizer

summary

This article goes from basic to advanced and then to actual combat. It makes MySQL clear and clear. This should be the best learning note about MySQL I have seen so far. I believe that if you carefully read this note, you can easily solve the problems encountered at work or asked by the interviewer!

Important: if you need to get the full version of MySQL learning notes, please forward it + pay attention Click here for free To free download!

Sorting of 50 high-frequency interview questions on MySQL:

Keywords: Java Eclipse Back-end Programmer unit testing

Added by Traveller29 on Sat, 18 Dec 2021 16:26:50 +0200