This is the 14th day of my participation in Gwen Challenge

Author: Flower Gie

Wechat official account: Java development zero to one

preface

We have updated seven chapters of the multithreading series, and we strongly recommend that you learn them in order:

Squat can also enter the factory “multithreading series article directory

Whether it is Java or advanced, thread pool is basically a must in the interview question, although this knowledge involves many things, but not difficult to understand, very suitable for the interview score. In the last article “squat can also enter the factory” multi-threaded series – thread pool essence (must see), partners should have a basic understanding of the thread pool, the thread pool is not very understanding of the students, suggested to read a first.

This article will discuss how to make good use of the thread pool and its principle. It is essential to understand its internal structure if you want to get a better salary in an interview and troubleshoot problems in daily work.

Ps: This article uses JDK8 environment to explain

The body of the

Me: Doggie, after learning the last chapter, can you help me summarize the important components of the thread pool?

There are four main components of a thread pool:

  • Thread pool manager: used to manage thread pools, such as stopping thread pools, creating thread pools, etc.
  • Worker thread: used to read and execute tasks from a queue;
  • Task queue: Stores tasks that are too late to execute;
  • Task interface: a task that is executed one by one and stored in a task queue when not executed.

Me: Can you explain the family history of thread pools? So many categories are confusing me

There are a lot of classes involved in thread pooling, but it is not difficult to distinguish them. Let’s take a look at this structure.

  • Executor: is a top-level interface that contains only one internal interfaceexecute()Methods;
  • ExecutorService: is also an interface that inherits the Executor interface and has been addedshutdown(),submit()Methods;
  • ExecutorsThe: is a utility class that provides common thread creation methods, such as:newSingleThreadExecutor,newFixedThreadPoolAnd so on.
  • ThreadPoolExecutor: Is a real thread pool.

Me: Soxna, it’s not that hard, but how do you submit tasks to a thread pool?

Florgie, you’re acting like an X in front of me. Let me teach you how to behave.

There are two ways to submit a task, but it’s essentially one, because submit ultimately calls the execute() method:

  • Execute () : Used to submit tasks that do not require a return value, which means there is no way to determine whether the task was successfully executed.
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
    service.execute(new Runnable() {
        @Override
        public void run(a) {}});Copy the code
  • Submit: The thread pool returns an object of type Future, which can be used to determine whether the execution was successful and to obtain the return value through the get() method.
ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
    Future<Object> future =(Future<Object>) service.submit(new Runnable() {
        @Override
        public void run(a) {
            System.out.println(1); }});try {
        future.get();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }finally {
        // Close the thread pool
        service.shutdown();
    }
Copy the code

Me: So easy, dog, do you have something exciting

With the clod, you can give me more clod. Now that the basic knowledge has been mastered, let’s take a closer look at the source code and understand the life of the thread pool in the way of source code.

So sudden? I don’t even know what the thread pool life cycle is……

There are five life cycles for thread pools:

  • RUNNING: Can accept new tasks and handle queuing tasks;
  • SHUTDOWN: no longer accepts new tasks, but processes queueing tasks.
  • STOP: does not accept new tasks, does not process queued tasks, and interrupts ongoing tasks.
  • TIDYING: All tasks have terminated, and when workworkerCount is zero, the thread transitions to this state and runsterminated()Functions;
  • TERMINATED: The function TERMINATED () is complete.

Me: Ali Gato O ‘Kassan, who created ~

Hey, florgie, you’re such a slob. Let’s get down to some important source code for thread pools, starting with the execute method mentioned above:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    / / step one
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    / / in step 2
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null.false);
    }
    / / step 3
    else if(! addWorker(command,false))
            reject(command);
}

Copy the code

We can divide the analysis into three major steps according to the conditions:

  • Step 1 Analysis

The third inning of the code has a CTL, which is used to record the state of the thread pool and the number of running threads.

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

If true, addWorker will be called to create a new worker thread and the current task (command) will be run. If the new thread fails, the CTL will be fetched again.

// Whether the number of running threads is smaller than the number of core threads
if (workerCountOf(c) < corePoolSize) {
    // Add a thread to the thread pool and add the current task to the new thread
    if (addWorker(command, true))
        return;
    // Failed to create thread, get CLT again.
    c = ctl.get();
}
Copy the code
  • Step 2

IsRunning: checks whether the thread pool isRunning

If the number of running threads is not less than the number of core threads, the following six substeps are performed:

When the thread pool is running and the number of running threads is larger than the number of core threads, the task is queued.
if (isRunning(c) && workQueue.offer(command)) {
    //2. Get the thread pool state
    int recheck = ctl.get();
    //3. If the pool is not running, remove the task from the queue
    if (! isRunning(recheck) && remove(command))
        //4. Execute the reject policy
        reject(command);
    //5. Check whether the number of running threads is 0
    else if (workerCountOf(recheck) == 0)
        //6. Create a thread and add it to the thread pool
        addWorker(null.false);
}
Copy the code
// Remove the task
public boolean remove(Runnable task) {
    boolean removed = workQueue.remove(task);
    tryTerminate(); // In case SHUTDOWN and now empty
    return removed;
}
Copy the code
  • Step 3

If the first several conditions are not met, that is, when the number of running threads is greater than the number of core threads and the queue is full, addWorker will be called to create a new thread to perform the current task. If the creation fails, it means that the number of running threads has reached the maximum number of threads and new threads cannot be created again. At this time, the rejection policy will be implemented.

// Create a thread into the thread pool and run the current task.
else if(! addWorker(command,false))
    // If the number of running threads exceeds the maximum number, the task will be rejected if it fails
    reject(command);
Copy the code

The addWorker method is used several times above, so let’s take a look at its implementation logic.

Here is a summary and attached part of the source notes, small friends chew up, slightly longer:

  • AddWorker (command, true) : Creates a core thread and runs the task when the number of threads is less than corePoolSize.
  • addWorker(command, false): When the number of core threads is full, the blocking queue is full, and the number of threads is less thanmaximumPoolSizeCreate a non-core thread and run the task.
  • AddWorker (null, false) : If the worker thread is 0, create a core thread without running the task. (This is mainly to avoid that there are still tasks in the work queue, but the worker thread is 0, so that the work queue has not been executed.)
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        // Get the thread pool state and the number of running threads.
        int c = ctl.get();
        // Get the running status of the thread pool
        int rs = runStateOf(c);
        // The thread pool is closed, the current task is null, the queue is not empty, and the bucket returns a failure
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null&&! workQueue.isEmpty()))return false;

        for (;;) {
            // Get the number of threads in the thread pool
            int wc = workerCountOf(c);
            // If the number of threads exceeds CAPACITY, return false;
            // If core is true, the number of running threads is compared to the number of core threads; if core is false, it is compared to the maximum number of threads.
            If the number of threads running is greater than or equal to core, return false
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            // Try to increase the number of threads and, if successful, break out of the first for loop
            if (compareAndIncrementWorkerCount(c))
                break retry;
            // If the number of threads fails to increase, the CTL is fetched again
            c = ctl.get();
            // If the current running state is not equal to rs, the state has been changed.
            // return the first for loop to continue execution
            if(runStateOf(c) ! = rs)continueretry; }}boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // Create the Worker object based on the current task
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if(t ! =null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // After acquiring the lock, re-check the thread pool state
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    // Add the newly created thread to the thread pool
                    workers.add(w);
                    int s = workers.size();
                    // Records the maximum number of threads that have ever appeared in the thread pool
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true; }}finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // Start the thread to start running the task
                t.start();
                workerStarted = true; }}}finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

Copy the code

Me: This wave… I’m probably going to be gnawing all weekend, so how do I close the thread pool at the end?

There are two ways to close a running thread pool:

  • Shutdown: Set the state of the thread pool to shutdown and then stop all threads that are not executing tasks.
  • ShutdownNow:By iterating through the worker threads in the thread pool and calling them one by oneinterruptMethod to interrupt the thread, and may never terminate tasks that cannot respond to interrupts.shutdownNowThe thread pool state is first set toSTOPThen try to stop all threads that are executing or suspending tasks and return to the list of tasks waiting to be executed.

Either way, the isShutdown method will return true. The thread pool will be closed when all tasks have been shut down, and isTerminaed will return true.

Which method we should call to shutdown the thread pool depends on the nature of the task we add to the thread pool. If the task does not require complete execution, shutdownNow can be called, but shutdown is usually used to shutdown the thread pool.

conclusion

If you have any questions, please leave a comment below. If you have any questions, please leave a comment below.

Pay attention to avoid getting lost

The above is the whole content of this issue, if there is any mistake, please leave a message for advice, thank you very much. I’m GieGie, feel free to leave a comment and we’ll see you next time at 🦮.

The article continues to update, you can wechat search Java development zero to one for the first time to read, and you can get interview materials learning video, interested partners welcome to pay attention to, learn together, ha 🐮🥃.

Original is not easy, how can you bear to whoring for nothing, if you think this article is a little useful to you, thank old tie for this article to like, comment or forward, because this will be my output more quality articles of power, thank you!

Reference links:

Xujiajia.blog.csdn.net/article/det…

Blog.csdn.net/heihaozi/ar…

Blog.csdn.net/sihai12345/…

Ifeve.com/java-thread…