ThreadPoolExecutor thread pool (JUC source code) thread pool (JUC source code)

1. What do you understand about thread pools?

A: Thread pool combines elements such as locks, threads, queues and so on. In a large number of requests, multi-threaded requests can be processed, making full use of system resources and improving the speed of processing requests. More details can be expanded from the following aspects:

  1. ThreadPoolExecutor class structure
  2. ThreadPoolExecutor coreSize, maxSize and other important attributes
  3. The important role of Worker
  4. Submit the entire process

2. The relationship between ThreadPoolExecutor, Executor, ExecutorService, Runnable, Callable, FutureTask?

Answer: The above six classes can be divided into two categories: one is to define the task class, the other is to perform the task class.

  1. Define task classes: Runnable, Callable, FutureTask. Runnable is defined as a task with no return value, Callable is defined as a task with return value, FutureTask is the unification of Runnable and Callable tasks, and adds the task management function.
  2. Executables: ThreadPoolExecutor, Executor, and ExecutorService. Executtor defines the basic running interface, which is supplemented by the ExecutorService. ThreadPoolExecutor provides a truly runnable thread pool class, three classes that define how tasks work.

The common practice is to define tasks according to the definition task class and then throw the execution task class to execute them.

3. What is the role of queues in thread pools?

A: The functions are as follows:

  1. When the number of requests is larger than coreSize, tasks can be queued in a queue, so that the threads in the thread pool slowly consume requests. In practice, the actual number of threads cannot be equal to the number of requests. Queues provide a mechanism for tasks to queue, acting as a buffer.
  2. When the thread consumes all the threads, it will block to get data from the queue. Through the function of queue blocking, the thread will not die. Once there is data in the queue, it can be consumed immediately.

4. What do thread pool constructor parameters mean and behave like?

Answer: The meanings of the thread pool constructor parameters are as follows:

  1. CoreSize: number of core threads
  2. MaxSize: indicates the maximum number of threads
  3. KeepAliveTime: indicates the maximum idle time of a thread
  4. Queue: There are multiple queues to choose from, such as:
    1. ArrayBlockingQueue, a bounded queue, prevents resources from running out
    2. LinkedBlockingQueue, an unbounded queue in which unconsumed tasks can wait
    3. SynchronousQueue, in order to avoid task rejection, requires an unbounded maxSize of the thread pool, with the disadvantage of unbounded thread growth when tasks are submitted faster than they can be consumed
  5. ThreadFactory: 6. The ThreadFactory created by a thread can be customized. You can also use the default DefaultThreadFactory. When creating a thread using DefaultThreadFactory, the priority is limited to NORM_PRIORITY, which is set to non-daemon by default.
  6. Rejection strategy: Use the RejectedExecutionHandler class to trap exceptions when the Executor is shut down or when the maximum thread and queue are saturated. There are four types of rejection strategies:
    1. ThreadPoolExecutor. AbortPolicy: throw an exception
    2. Discarding the task ThreadPoolExecutor. DiscardPolicy: directly
    3. ThreadPoolExecutor. CallerRunsPolicy: do not use the thread pool, the main thread to execute
    4. ThreadPoolExecutor. DiscardOldestPolicy: discarding the oldest task in the thread pool

5. What does each parameter do when requests are increasing?

  1. < coreSize: create a new thread to process the task;
  2. CoreSize <= number of requests && can be queued successfully: the task is queued to be consumed;
  3. Queue full && number of requests < maxSize: create a new thread to process the task;
  4. Queue full && Number of requests >= maxSize: Reject the request using RejectedExecutionHandler.

6. Can coreSize and maxSize be set dynamically? Are there any rules?

A: Generally, coreSize and maxSize are set when the thread pool is initialized, but we can change these values dynamically with setCorePoolSize and setMaximumPoolSize methods.

setCorePoolSize

// If the new value is less than coreSize, the extra threads will be reclaimed when idle.
// if it is larger than coseSize, a new thread will be created
public void setCorePoolSize(int corePoolSize) {
    if (corePoolSize < 0)
        throw new IllegalArgumentException();
    int delta = corePoolSize - this.corePoolSize;
    this.corePoolSize = corePoolSize;
    // The number of active threads is greater than the newly set number of core threads
    if (workerCountOf(ctl.get()) > corePoolSize)
        // Try to interrupt the worker that can acquire the lock, loop only once
        // Finally, there is no guarantee that the number of active threads is less than the number of core threads
        interruptIdleWorkers();
    // Set the number of core threads to be greater than the original number of core threads
    else if (delta > 0) {
        // It is not clear how many new threads should be added. Take the minimum number of new core threads and wait queue data
        int k = Math.min(delta, workQueue.size());
        // A new thread is added up to k, and no new thread is added if the queue is empty
        while (k-- > 0 && addWorker(null.true)) {
            if (workQueue.isEmpty())
                break; }}}Copy the code

SetMaximumPoolSize = setMaximumPoolSize

// If maxSize is larger than the original value, set it directly.
// If maxSize is smaller than the original value, try to kill some workers
public void setMaximumPoolSize(int maximumPoolSize) {
    if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
        throw new IllegalArgumentException();
    this.maximumPoolSize = maximumPoolSize;
    if (workerCountOf(ctl.get()) > maximumPoolSize)
        interruptIdleWorkers();
}
Copy the code

7. KeepAliveTime is set to negative or 0, indicating infinite blocking.

A: No, if keepAliveTime is set to negative, IllegalArgumentException will be reported directly when thread pool initialization. If keepAliveTime is set to 0, the queue will be LinkedBlockingQueue. When the workqueue.poll (keepAliveTime, timeUnit.nanoseconds) method is executed, null is returned if there are no tasks in the queue, causing the thread to return immediately and not block indefinitely.

8. Thread recycling time, how to reflect the source code?

A: The timing of idle thread collection:

  1. If a thread fails to retrieve a task from the blocking queue after the keepAliveTime period has elapsed (this condition is called thread idle), the current thread is reclaimed
  2. If allowCoreThreadTimeOut is set to True, core threads are also reclaimed until there is one thread left
  3. If allowCoreThreadTimeOut is set to false, only threads that are not core threads are reclaimed.

If a thread fails to get a task from the queue after keepAliveTime, the block is broken, the thread returns, and the lifetime of the thread ends. The JVM reclaims the thread object. So thread recycling source code is to let the thread is not blocked in the queue, directly returned, this can refer to the source code.

9. Can thread creation in the thread pool be customized to reject requests? How to customize?

A: It can be customized. By default, thread creation uses the DefaultThreadFactory interface. The RejectedExecutionHandler interface can also be used to reject requests. When ThreadPoolExecutor is initialized, you simply pass two custom classes to ThreadPoolExecutor as input arguments to the constructor.

10. What if I want to do some resource cleaning before and after thread pool tasks are executed?

A: ThreadPoolExecutor provides some hook functions. We simply inherit ThreadPoolExecutor and implement these hook functions. Implement the beforeExecute method before thread pool task execution and afterExecute method after execution.