2
头图

Hello everyone, I'm Glacier~~

Today, let's briefly talk about the ThreadPoolExecutor class in the thread pool. Okay, not much more to say, let's start today's topic.

1. Throwing bricks to attract jade

Since Java supports the execution of corresponding tasks in a multi-threaded manner, why is thread pool technology provided in JDK1.5? We all make up our own minds on this question, and use more brains, there is definitely no harm, hahaha. . .

Speaking of thread pool technology in Java, it is involved in many frameworks and asynchronous processing middleware, and its performance has withstood a long test. It can be said that Java's thread pool technology is one of the core technologies of Java. In the field of Java's high concurrency, Java's thread pool technology is a topic that can never be avoided. Since Java's thread pool technology is so important (how can it be said that it is so important? That is quite important, that guy is very important, hahaha), then, in this article, we will briefly talk about the thread pool and the ThreadPoolExecutor class. As for the technical details in the thread pool and the underlying principles and source code analysis of ThreadPoolExecutor, we will conduct in-depth analysis in the [High Concurrency Special] column.

Introduction: This article is the beginning of the thread pool in high concurrency. I will not explain it in depth for the time being, but let you understand one of the core classes in the thread pool as a whole - ThreadPoolExecutor, about the underlying principle and source code implementation of ThreadPoolExecutor , as well as the underlying principles and source code implementation of other technical details in the thread pool, we will be dead in the next article of [High Concurrency Topic].

Second, the disadvantages of Thread directly creating threads

(1) Each time a new Thread creates a new object, the performance is poor.
(2) There is a lack of unified management of threads, and there may be unlimited new threads, competing with each other, which may occupy too many system resources and cause a crash or OOM.
(3) Lack of more functions, such as more execution, regular execution, thread interruption.
(4) For other drawbacks, everyone can make up their own brains and use their brains more. There is no harm, haha.

3. The benefits of thread pools

(1) Reuse existing threads, reduce the overhead of object creation and death, and have good performance.
(2) It can effectively control the maximum number of concurrent threads, improve system resource utilization, and avoid excessive resource competition and blockage.
(3) Provide functions such as timed execution, periodic execution, single thread, and concurrent number control.
(4) Provide a method to support thread pool monitoring, which can monitor the resources of the thread pool in real time.
(5) Other benefits, everyone can make up their own brains, use more brains, there is no harm, haha.

Fourth, the thread pool

1. Thread pool class structure relationship

The structural relationship of some interfaces and classes in the thread pool is shown in the following figure.

The underlying principles and source code of these interfaces and classes will be discussed later.

2. Create a commonly used class for thread pools - Executors

  • Executors.newCachedThreadPool: Create a cacheable thread pool. If the size of the thread pool exceeds the need, it can flexibly recycle idle threads. If there are no recyclable threads, create a new thread
  • Executors.newFixedThreadPool: Create a fixed-length thread pool, which can control the maximum number of concurrent threads, and the excess threads will wait in the queue
  • Executors.newScheduledThreadPool: Create a fixed-length thread pool to support scheduled and periodic task execution
  • Executors.newSingleThreadExecutor: Create a single-threaded thread pool, use a unique worker thread to execute tasks, and ensure that all tasks are executed in the specified order (first-in, first-out or priority)
  • Executors.newSingleThreadScheduledExecutor: Create a single-threaded thread pool that supports scheduled and periodic task execution
  • Executors.newWorkStealingPool: Create a work-stealing thread pool with parallelism level

3. Several states of the thread pool instance

  • Running: Running state, can receive newly submitted tasks, and can also process tasks in the blocking queue
  • Shutdown: Closed state, can no longer receive newly submitted tasks, but can process tasks that have been saved in the blocking queue. When the thread pool is in the Running state, calling the shutdown() method will cause the thread pool to enter this state
  • Stop: Cannot receive new tasks, and cannot process tasks that have been saved in the blocking queue. It will interrupt the thread that is processing the task. If the thread pool is in the Running or Shutdown state, calling the shutdownNow() method will cause the thread pool to enter this state.
  • Tidying: If all tasks have been terminated, the effective number of threads is 0 (the blocking queue is empty, and the number of worker threads in the thread pool is 0), the thread pool will enter this state.
  • Terminated: The thread pool in the Tidying state calls the terminated() method, and the thread pool will be used to enter this state

Note: There is no need to do special processing for the status of the thread pool. The status of the thread pool is defined and processed by the thread pool itself according to the method.

4. Some suggestions for rationally configuring threads

(1) For CPU-intensive tasks, you need to squeeze the CPU as much as possible. The reference value can be set to NCPU+1 (the number of CPUs plus 1).
(2) For IO-intensive tasks, the reference value can be set to 2*NCPU (the number of CPUs multiplied by 2)

Five, one of the core classes of the thread pool - ThreadPoolExecutor

1. Construction method

The constructor with the most parameters of ThreadPoolExecutor is as follows:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler rejectHandler)

All other construction methods are called by this construction method to instantiate the object. It can be said that after we directly analyze this method, we also understand what is going on with the other construction methods! Next, this construction method is analyzed in detail.

Note: In order to analyze the construction method of the ThreadPoolExecutor class more deeply, the order of parameters will be adjusted appropriately for analysis, so that you can better understand the role of each parameter in the construction method of ThreadPoolExecutor.

The above constructor receives the following parameters for initialization:

(1) corePoolSize: The number of core threads.

(2) maximumPoolSize: the maximum number of threads.

(3) workQueue: The blocking queue, which stores tasks waiting to be executed, is very important and will have a significant impact on the running process of the thread pool.

Among them, the relationship of the above three parameters is as follows:

  • If the number of running threads is less than corePoolSize, new threads are created directly to process tasks, even if other threads in the thread pool are idle.
  • If the number of running threads is greater than or equal to corePoolSize and less than maximumPoolSize, a new thread processing task will be created only when the workQueue is full.
  • If the set corePoolSize is the same as the maximumPoolSize, the size of the created thread pool is fixed. At this time, if a new task is submitted and the workQueue is not full, the request is put into the workQueue, waiting for an idle thread, from the workQueue Take out the task for processing.
  • If the number of running threads is greater than the maximumPoolSize, and at the same time, the workQueue is full, the processing strategy will be specified through the rejection strategy parameter rejectHandler.

According to the configuration of the above three parameters, the thread pool will process the tasks as follows:

When submitting a new task to the thread pool, the thread pool will decide how to process the task according to the number of threads running in the current thread pool. There are three processing methods: direct switching, using an infinite queue, and using a bounded queue.

  • The commonly used queue to switch directly is SynchronousQueue.
  • Using an infinite queue is to use a queue based on a linked list, such as: LinkedBlockingQueue. If this method is used, the maximum number of threads created in the thread pool is corePoolSize. At this time, maximumPoolSize will not work. When all core threads in the thread pool are running, new tasks are submitted and placed in the waiting queue.
  • The ArrayBlockingQueue is used to use the bounded queue. In this way, the maximum number of threads in the thread pool can be limited to maximumPoolSize, which can reduce resource consumption. However, this method makes it more difficult for the thread pool to schedule threads, because the capacity of the thread pool and queue is limited.

According to the above three parameters, we can simply draw some measures on how to reduce system resource consumption:

  • If you want to reduce system resource consumption, including CPU usage, operating system resource consumption, and context switching overhead, you can set a larger queue capacity and a smaller thread pool capacity. This reduces the throughput of thread processing tasks.
  • If the submitted tasks are often blocked, you can consider calling the method for setting the maximum number of threads to reset the maximum number of threads in the thread pool. If the capacity of the queue is set to be small, it is usually necessary to set the capacity of the thread pool to be larger, so that the CPU usage will be higher. If the capacity of the thread pool is set too large, the concurrency will increase, and you need to consider the issue of thread scheduling, which may reduce the throughput of processing tasks.

Next, we continue to look at the parameters of the constructor of ThreadPoolExecutor.

(4) keepAliveTime: How long will the thread be terminated when there are no tasks to execute. When the number of threads in the thread pool is greater than corePoolSize, if no new tasks are submitted at this time, the threads outside the core thread will not be destroyed immediately, and need to wait until waiting The time exceeds the keepAliveTime will terminate.

(5) unit: the time unit of keepAliveTime

(6) threadFactory: thread factory, which is used to create a thread. By default, a default factory is provided to create a thread. When the default factory is used to create a thread, the newly created thread will have the same priority and be a non-guardian thread. , and also sets the name of the thread

(7) rejectHandler: strategy for refusing to process tasks

If the workQueue blocking queue is full and there is no idle thread pool, at this time, to continue submitting tasks, a strategy needs to be adopted to process this task.

The thread pool provides a total of four strategies:

  • Throwing an exception directly is also the default strategy. The implementation class is AbortPolicy.
  • Use the thread where the caller is located to execute the task. The implementation class is CallerRunsPolicy.
  • Discard the top task in the queue and execute the current task. The implementation class is DiscardOldestPolicy.
  • Directly discard the current task. The implementation class is DiscardPolicy.

2. Methods provided by ThreadPoolExecutor to start and stop tasks

(1) execute(): Submit the task and hand it over to the thread pool for execution (2) submit(): Submit the task and return the execution result execute+Future
(3) shutdown(): close the thread pool and wait for all tasks to be executed (4) shutdownNow(): close the thread pool immediately without waiting for the task to be executed

3. Methods provided by ThreadPoolExecutor for monitoring

(1) getTaskCount(): the total number of executed and unexecuted tasks in the thread pool (2) getCompletedTaskCount(): the number of completed tasks (3) getPoolSize(): the current number of threads in the thread pool (4) getCorePoolSize(): the thread Number of core threads in the pool (5) getActiveCount(): The number of threads executing tasks in the current thread pool

Okay, let's stop here today, I'm Glacier, see you next time~~


冰河
156 声望970 粉丝