(Mobile phone landscape view source more convenient)


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

Introduction to the

The ThreadPoolExecutor constructor is an entry point for creating a thread pool. It is simple, but it is very informative, and it can lead to a number of questions, which are also frequently asked in interviews. If you can answer all of these questions, you can skip the analysis below.

The problem

(1) How many constructors does ThreadPoolExecutor have?

(2) How many arguments does the longest constructor of ThreadPoolExecutor take?

(3) What is keepAliveTime for?

(7) Will the core thread timeout shut down? Can I close it over time?

(4) Can ConcurrentLinkedQueue be used as a parameter of the task queue?

(5) How is the default thread created?

(6) How to implement their own thread factory?

(7) What are the rejection strategies?

(8) What is the default rejection policy?

A constructor

All right, 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;
}
Copy the code

ThreadPoolExecutor has four constructors, the first three of which end up calling the last one, which takes seven arguments, They are corePoolSize, maximumPoolSize, keepAliveTime, Unit, workQueue, threadFactory, and Handler.

corePoolSize

Number of core threads.

When the number of running threads is smaller 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 is not created but thrown to the task queue.

maximumPoolSize

Maximum number of threads.

When the task queue is full, only one non-core thread is created for each task, but the maximum number of threads cannot be exceeded.

keepAliveTime + unit

How long and in units a thread remains idle.

By default, these two parameters are valid only 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 also works for core threads.

That is, when the task queue is empty, how long the thread will hold before it is destroyed, internally mainly by blocking the queue with a timeout poll(timeout, unit) method.

workQueue

Task queues.

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

This queue must be a blocking queue, so something like ConcurrentLinkedQueue cannot be used as a parameter because it is a concurrency-safe queue, but 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 by the public account "Tongge read source code" original]
}
Copy the code

threadFactory

Thread factories.

By default, the DefaultThreadFactory class from the Executors tool class is used. The thread names are automatically generated and cannot be customized to distinguish between thread pools, and they are non-daemon threads.

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 finalString 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);returnt; }}Copy the code

So how do you customize a thread factory?

It’s as simple as implementing a ThreadFactory yourself and passing in the name and whether it’s a daemon or not as arguments to the constructor.

Those interested 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
Copy the code

handler

Rejection strategy.

The reject policy indicates that when the task queue is full and the number of threads reaches the maximum, the thread pool can no longer handle new tasks. What logic should be used to process new tasks?

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

The default rejection strategy is to throw an exception, that is, the thread pool is no longer able to hold, and the caller will throw an exception if he or she adds a task to it.

The default reject strategy is relatively simple, but it is significantly better than the discard task strategy. At least the caller can catch the exception and process it again.

eggs

OK, do you have any questions about the constructor of ThreadPoolExecutor? Welcome to leave a comment, private chat hook up.


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.