Deep analysis of thread pool in java thread series -- construction method

Keywords: Java Netty Google Mobile

(it's more convenient to see the source code on the horizontal screen of mobile phone)

Note: java source code analysis part is based on java version 8 unless otherwise specified.

brief introduction

The construction method of ThreadPoolExecutor is to create the entry of thread pool. Although it's relatively simple, it has a large amount of information, which can also cause a series of questions. Similarly, it's also a question often asked in interviews. Brother Tong just lists some questions about the construction method of ThreadPoolExecutor. If you can answer them all, you don't need to look at the following points Analysis.

problem

(1) how many construction methods are there for ThreadPoolExecutor?

(2) how many parameters are there in the longest construction method of ThreadPoolExecutor?

(3) what is keepAliveTime for?

(7) will the core thread timeout? Can it be closed over time?

(4) can ConcurrentLinkedQueue be a parameter of task queue?

(5) how is the default thread created?

(6) how to realize your own thread factory?

(7) what are the rejection strategies?

(8) what is the default rejection policy?

Construction method

OK, let's go straight to the code.

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

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

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

ThreadPoolExecutor has four construction methods, the first three of which are the last to call. It has seven parameters, namely corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory and handler.

corePoolSize

Number of core threads.

When the number of running threads is less than the number of core threads, a task creates a core thread;

When the number of running threads is greater than or equal to the number of core threads, the task will be dropped into the task queue instead of creating threads.

maximumPoolSize

Maximum number of threads.

When the task queue is full, this article was originally created by the public slave number "tongge read source code". A task creates a non core thread, but it cannot exceed the maximum number of threads.

keepAliveTime + unit

Thread idle time and unit.

By default, these two parameters are only valid when the number of running threads is greater than the number of core threads, that is, only for non core threads.

However, if allowCoreThreadTimeOut is set to true, it is also valid for core threads.

That is to say, when the task queue is empty, how long does the thread stay before it is destroyed? Internally, it is mainly realized by blocking the poll(timeout, unit) method with timeout.

workQueue

Task queue.

When the number of running threads is greater than or equal to the number of core threads, the task comes into the task queue first.

This queue must be a blocking queue, so a queue like ConcurrentLinkedQueue cannot be used as a parameter, because although it is a concurrent and secure queue, it is not a blocking queue.

// ConcurrentLinkedQueue does not implement the BlockingQueue interface
public class ConcurrentLinkedQueue<E> extends AbstractQueue<E>
        implements Queue<E>, java.io.Serializable {
    // ..., this article is originated by the public subordinate number "Tong Ge read the source code"
}

threadFactory

Thread factory.

The DefaultThreadFactory class in the Executors tool class is used by default. There is a disadvantage in this class. The name of the created thread is automatically generated. It cannot be customized to distinguish different thread pools, and they are all non daemons.

static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }
    }

How to customize a thread factory?

In fact, it is also very simple to implement a ThreadFactory, and then pass in the name and whether it is a daemons as parameters of the construction method.

Interested students can refer to the default thread factory in netty or the thread factory in google.

io.netty.util.concurrent.DefaultThreadFactory
com.google.common.util.concurrent.ThreadFactoryBuilder

handler

Reject policy.

The rejection policy indicates that when the task queue is full and the number of threads reaches the maximum, the thread pool can no longer afford to add new tasks. What logic should these new tasks be handled according to.

Common rejection strategies include discarding the current task, discarding the oldest task, throwing an exception, and the caller handles the wait himself.

The default rejection policy is to throw an exception, that is, the thread pool cannot be hosted, and the caller will throw an exception when adding a task to it.

Although the default rejection strategy is simple and crude, it is much better than the discard task strategy. At least the caller can catch the exception and handle it again.

Egg

OK, we have a deep analysis on the construction method of ThreadPoolExecutor today. What other questions do you have about this? Welcome to discuss with brother Tong in private.

Posted by cyberRobot on Mon, 04 Nov 2019 16:18:44 -0800