(Mobile phone landscape view source more convenient)

Note: The Java source analysis section is based on the Java 8 version unless otherwise noted.

Note: The thread pool source section refers to the ThreadPoolExecutor class unless otherwise specified.

Introduction to the

In the last chapter we went over the life cycle of a thread (remember the six states?). , but did you know that thread pools also have a life cycle? !

The problem

(1) What are the states of the thread pool?

(2) What are the effects of various states on the tasks in the task queue?

On the first source

In fact, when we talked about thread pool architecture, we talked about methods, such as shutDown()/shutDownNow(), that are associated with the lifecycle of the thread pool.

Let’s start by looking at the states and related methods in the lifecycle defined in thread Pool ThreadPoolExecutor:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static final int COUNT_BITS = Integer.SIZE - 3; // =29 private static final int CAPACITY = (1 << COUNT_BITS) - 1; / / = 000, 11111... // runState is stored in the high-order bits private static final int RUNNING = -1 << COUNT_BITS; / / 111 00000... private static final int SHUTDOWN = 0 << COUNT_BITS; / / 000 00000... private static final int STOP = 1 << COUNT_BITS; / / 001 00000... private static final int TIDYING = 2 << COUNT_BITS; / / 010 00000... private static final int TERMINATED = 3 << COUNT_BITS; / / 011 00000... Private static int runStateOf(int c) {return c & ~CAPACITY; private static int runStateOf(int c) {return c & ~CAPACITY; } private static int workerCountOf(int c) {return c & CAPACITY; } / / calculate the value of the CTL is equal to the running state "and" number of threads private static int ctlOf (int the rs, int wc) {return rs | wc. }Copy the code

From the above code, we can conclude:

(4) The state of the thread pool and the number of worker threads are stored in CTL, similar to the state variable in AQS, where AtomicInteger is directly used.

(2) The three bits of CTL save the running state, and the lower 29 bits save the number of working threads, that is, the number of threads can only have (2^29-1) at most, that is, CAPACITY above;

The thread pool state is RUNNING, SHUTDOWN, STOP, TIDYING, and TERMINATED.

(4) RUNNING: new tasks can be accepted and tasks in the queue can be executed;

(5) SHUTDOWN: no new task is accepted, but tasks in the queue can be executed;

(6) STOP: no new task is accepted, no task in the queue is executed, and the ongoing task is interrupted.

In TIDYING, all tasks are terminated and the number of working threads is 0. The terminated thread will execute the terminated() hook method and only one thread will execute the method.

State TERMINATED, the hook method TERMINATED () is TERMINATED;

The flow chart

Let’s look at how these states flow from one state to another:

(1) When creating a new thread pool, its initial state is RUNNING. This can be seen in the CTL definition above.

(2) RUNNING->SHUTDOWN;

(3) RUNNING->STOP;

(4) SHUTDOWN->STOP, execute shutdownNow() method;

STOP->TIDYING. Terminated () is executed when shutdown() or shutdownNow() is terminated and the number of working threads terminated is 0.

TERMINATED (6) TIDYING->TERMINATED ();

Source code analysis

Do you think post a state of the source code, draw a picture is over? That certainly can’t ah, let’s have a look at the source code is how to control.

(1) RUNNING

When creating a thread pool, CTLS are initialized to the RUNNING state, so the initial state of the thread pool is RUNNING.

Private final AtomicInteger CTL = new AtomicInteger(ctlOf(RUNNING, 0));Copy the code

(2) SHUTDOWN

Change the state to shutdown when executing the shutdown() method, which is guaranteed to succeed because advanceRunState() is a spin and will not exit until it succeeds.

public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { checkShutdownAccess(); // Change the state to SHUTDOWN advanceRunState(SHUTDOWN); // Mark idle threads as interrupted; interruptIdleWorkers(); onShutdown(); } finally { mainLock.unlock(); } tryTerminate(); } private void advanceRunState(int targetState) { for (;;) { int c = ctl.get(); // If the state is greater than SHUTDOWN or changed to SHUTDOWN successfully, To break out of the spin if (runStateAtLeast (c, targetState) | | ctl.com pareAndSet (c, ctlOf (targetState, workerCountOf (c)))) break; }}Copy the code

(3) STOP

When the shutdownNow() method is executed, the thread pool state is changed to STOP and all threads are marked as interrupted.

public List<Runnable> shutdownNow() {
    List<Runnable> tasks;
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        // 修改为STOP状态
        advanceRunState(STOP);
        // 标记所有线程为中断状态
        interruptWorkers();
        tasks = drainQueue();
    } finally {
        // 【本文由公从号“彤哥读源码”原创】
        mainLock.unlock();
    }
    tryTerminate();
    return tasks;
}Copy the code

Whether threads respond to interrupts is actually done in the queue’s take() or poll() methods, and eventually in the AQS, which detect thread interrupts and throw an InterruptedException, which is then caught in getTask(). At the next spin, exit the current thread and reduce the number of worker threads.

private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // If the state is stopped, it will exit the loop directly, And reduce the number of threads / / dropped out of the loop is equivalent to the thread to the end of the life cycle of the if (rs > = SHUTDOWN && (rs > = STOP | | workQueue. IsEmpty ())) {decrementWorkerCount ();  return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try {Runnable r = timed in poll() or take(). workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r ! = null) return r; timedOut = true; } catch (InterruptedException retry) {timedOut = false; }}}Copy the code

One problem here is that what about the tasks that are already taken out and returned by getTask()?

They actually execute normally, but if you’re interested you can look at the runWorker() method for yourself, which we’ll examine in the next section.

(4) TIDYING

After shutdown() or shutdownNow() is executed, this state is entered if all tasks have been aborted and the number of worker threads is zero.

final void tryTerminate() { for (;;) { int c = ctl.get(); // The following cases do not execute the following code // 1. The value of the state is greater than that of TIDYING, Is TERMINATED / / 3. The SHUTDOWN state and task queue is empty if not (set (c) | | runStateAtLeast (c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; If (workerCountOf(c)! // Try to interrupt idle threads interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); // CAS is in TIDYING state. If (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {try {// The update is successful. Terminated hook method terminated() is executed. } finally {// force the update state to TERMINATED, CAS is no longer needed in TERMINATED ctl.set(ctlOf(TERMINATED, 0)); termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }Copy the code

The code that is actually updated in TIDYING and TERMINATED state is in the tryTerminate() method. The tryTerminated() method is called in many places, such as shutdown(), shutdownNow(), and when the thread exits. So almost every thread will call tryTerminate() when it finally dies, but only one thread will actually execute to the point where its status is changed to TIDYING.

The terminated() method is executed after state terminated to TIDYING and terminated to signal that the thread pool is dead.

(5) TERMINATED

See analysis in TIDYING.

eggs

In this chapter, we learned about the thread pool lifecycle from the perspectives of state definition, flowcharts, source code analysis, etc. How well did you master it?

In the next chapter, we will start to learn the main process of thread pool execution. If you are afraid of this part, you can first read the two articles written by Tong GUI on “Handwritten thread pool”. It is very beneficial to learn the main process of thread pool next.

Welcome to pay attention to my public number “Tong Elder brother read source code”, view more source code series articles, with Tong elder brother tour the ocean of source code.