laitimes

Java-thread pool in detail

author:Programming Junior

1. How to create it

1.1 newFixedThreadPool

// 创建固定线程数是5的线程池·
ExecutorService executorService= Executors.newFixedThreadPool(5);           

1.1.1 Introduction

FixedThreadPool is known as a thread pool that can be reused with a fixed number of threads. Take a look at the implementation with the relevant source code in the Executors class:

1.1.1.2 Execution Process

Java-thread pool in detail

1.1.1.3 Cons

  • Using LinkedBlockingQueue, it is easy to picture OOM
  • The number of threads in the thread pool is fixed, and it is easy to take the CPU as idle.

1.1.2 newSingleThreadExecutor

// 创建线程数是1的线程池 
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();           

1.1.2.1 Introduction

SingleThreadExecutor is a thread pool with only one thread. Here's a look at the implementation of SingleThreadExecutor:

1.1.2.2 Execution Process

Java-thread pool in detail

1.1.2.3 Disadvantages

  • Using LinkedBlockingQueue, it is easy to picture OOM.
  • Mistaken as CPU idle waste.

1.1.3 newCachedThreadPool

// 创建无固定数量的线程池
ExecutorService executorService= Executors.newCachedThreadPool();           

1.1.3.1 Introduction

The CachedThreadPool is a thread pool that creates new threads as needed. Let's take a look at the implementation of CachedThreadPool through the source code:

1.1.3.2 Execution Process

Java-thread pool in detail
  1. First execute SynchronousQueue.offer(Runnable task) to submit the task to the task queue. If there is a free thread in the current maximumPool executing SynchronousQueue.poll(keepAliveTime,TimeTime.NANOSECONDS), then the main thread executing the offer operation is successfully paired with the poll operation executed by the idle thread, the main thread gives the task to the idle thread to execute, and the execute() method is executed, otherwise step 2 below is executed.
  2. When the initial maximumPool is empty or there are no free threads in the maximumPool, no threads will execute SynchronousQueue.poll(keepAliveTime,TimeTime.NANOSECONDS). In this case, step 1 fails, and the CachedThreadPool creates a new thread to execute the task, and the execute method executes.

1.1.3.3 Disadvantages

  • The number of threads allowed to be created, Integer.MAX_VALUE, will be displayed as OOM.

1.1.4 ThreadPoolExecutor

int coreSize = 10;
int maximumPoolSize = 20;
int keepAliveTime = 5;
TimeUnit unit = TimeUnit.MINUTES;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue(50);
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("xxx").build();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ExecutorService executorService = new ThreadPoolExecutor(coreSize,maximumPoolSize, keepAliveTime, unit, workQueue, factory, handler);           

The thread pool is created by constructing methods.

1.1.4.1 Introduction

ThreadPoolExecutor is the core class of the thread pool implementation, and the other three creation methods above are ultimately created by calling its constructor, through which we can freely control the various parameters of the thread pool.

1.1.4.2 Execution Process

Java-thread pool in detail

1.1.4.3 Advantages

  1. Flexibility: The ThreadPoolExecutor constructor provides a wealth of parameter options, and the thread pool can be flexibly configured according to actual needs. You can specify parameters such as the number of core threads, the maximum number of threads, the idle time of the threads, and the task queue type to meet different concurrency scenarios.
  2. Concurrency control: The thread pool can effectively limit the number of concurrent threads and prevent excessive consumption of system resources. By setting the appropriate thread pool size, you can balance the concurrent load of the system and prevent the system from degrading or crashing due to excessive concurrent requests.

1.2 How to gracefully close the thread pool

private static void shutdown(ExecutorService executorService) {
    // 第一步:使新任务无法提交
    executorService.shutdown();
    try {
        // 第二步:等待未完成任务结束
        if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
             // 第三步:取消当前执行的任务
            executorService.shutdownNow();
            // 第四步:等待任务取消的响应
            if(!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                System.err.println("Thread pool did not terminate");
            }
        }
    } catch(InterruptedException ie) {
        // 第五步:出现异常后,重新取消当前执行的任务
        executorService.shutdownNow();
        Thread.currentThread().interrupt(); // 设置本线程中断状态
    }
}           

1.3 Configure the thread pool

1.3.1 Important parameters

1、corePoolSize:核心线程数
    * 核心线程会一直存活,及时没有任务需要执行
    * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
    * 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

2、queueCapacity:任务队列容量(阻塞队列)
    * 当核心线程数达到最大时,新任务会放在队列中排队等待执行

3、maxPoolSize:最大线程数
    * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
    * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

4、 keepAliveTime:线程空闲时间
    * 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
    * 如果allowCoreThreadTimeout=true,则会直到线程数量=0

5、allowCoreThreadTimeout:允许核心线程超时

6、rejectedExecutionHandler:任务拒绝处理器
    * 两种情况会拒绝处理任务:
        - 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
        - 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用             shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
    * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,           会抛出异常
    * ThreadPoolExecutor类有几个内部实现类来处理这类情况:
        - AbortPolicy 丢弃任务,抛运行时异常
        - CallerRunsPolicy 执行任务
        - DiscardPolicy 忽视,什么都不会发生
        - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
    * 实现RejectedExecutionHandler接口,可自定义处理器           

1.3.2 Saturation strategy

  1. AbortPolicy: In the default ThreadPoolExecutor.AbortPolicy, a rejection of a handler throws a runtime RejectedExecutionException.
  2. CallerRunsPolicy: In ThreadPoolExecutor.CallerRunsPolicy, the thread calls the execute itself that runs the task. This policy provides a simple feedback control mechanism that slows down the submission of new tasks.
  3. DiscardPolicy: In ThreadPoolExecutor.DiscardPolicy, tasks that cannot be executed are deleted.
  4. DiscardOldestPolicy: In ThreadPoolExecutor.DiscardOldestPolicy, if the executor has not been closed, the task at the head of the work queue is deleted and the executor is retried (repeat the process if it fails again).

1.4 Thread Pool Execution Procedure

Java-thread pool in detail
  1. When a running thread is smaller than corePoolSize, the thread pool creates a new thread.
  2. When it is larger than corePoolSize and the task queue is not full, the entire task is crammed into the queue.
  3. When larger than corePoolSize and the task queue is full, and less than maximumPoolSize, a new thread is created to execute the task.
  4. When larger than maximumPoolSize, threads are processed according to the deny policy.

1.5 How to set parameters

  • CPU-intensive: Try to use a small thread pool, generally +1 CPU cores
  • IO intensive: A larger thread pool can be used, generally CPU cores * 2

1.6 Thread Pool Monitoring

Monitoring is provided by the thread pool. There are some properties in the thread pool that can be used when monitoring the thread pool:

  • taskCount: The number of tasks that the thread pool needs to execute.
  • completedTaskCount: The number of tasks that the thread pool has completed during the running process, less than or equal to taskCount.
  • largestPoolSize: The maximum number of threads ever created by the thread pool. From this data, you can know if the thread pool is full. If it is equal to the maximum size of the thread pool, it means that the thread pool was once full.
  • getPoolSize: The number of threads in the thread pool. If the thread pool is not destroyed, the threads in the pool will not be automatically destroyed, so this size will only be increased + getActiveCount: to get the number of active threads.

Monitoring by extending the thread pool. By inheriting the thread pool and overriding the thread pool's beforeExecute, afterExecute, and terminated methods, we can do something before, after, and before the thread pool is shut down. For example, monitor the average execution time, maximum execution time, and minimum execution time of tasks.