The thread pool

In addition to the task execution time, threads also need to create, destroy, switch time, so infinite thread creation and destruction will cause meaningless waste of resources, thread pool can limit the number of threads, and you can set the number of core threads after the completion of the destruction.

ThreadPoolExecutor overview

ThreadPoolExecutor inherits AbstractExecutorService->ExecutorService->Executor. Executor and ExecutorService are interface classes. Executor:** defines an execute(Runnable) interface method for submitting tasks that do not return a value. ExecutorService:** defines a number of interface methods, including submit() to submit tasks and return results, and shutdown() to shutdown thread pools.

Therefore, thread pools are defined above the ExecutorService interface, which is provided in the SDKExecutors** used to create a simple thread pool such as:

  • Executors. NewCachedThreadPool (creating unbounded thread pool)
  • Executors. NewFixedThreadPool (int nThreads) (create fixed-size pool)
  • Create a single thread pool: * * * * * * *

ThreadPoolExecutor must set the number of cores, maximum number of threads, thread lifetime, blocking queue, thread factory, and denial policy, either unset or customized.

Number of cores and maximum number of threads

  • CorePoolSize: Indicates the number of core threads in a thread pool
  • MaximumPoolSize: Maximum number of threads in the thread pool
  1. If the number of thread pool tasks is <=corePoolSize number of cores, new threads are created in the thread pool to execute the tasks, and these threads are not destroyed when they finish their tasks, but instead execute the tasks or new tasks waiting on the blocking queue.

  2. If the start number of thread pool tasks >corePoolSize number of cores, the task is sent to the blocking queue until the blocking queue is full.

  3. If the number of tasks submitted by the thread pool is greater than corePoolSize and the blocking queue is full, but the number of created threads is less than maximumPoolSize, new threads are created to perform new tasks, but these are non-core threads and are destroyed.

Preloading cores: If we want to preload one more core thread, we call the prestartCoreThread method, and if we want to preload all core threads, we call the prestartAllCoreThreads method.

Thread lifetime

  • KeepAliveTime: indicates the keepAliveTime
  • TimeUnit: indicates the unit of time

Non-core threads will be destroyed if they are idle for longer than the specified lifetime.

Queuing queue

The blocking queue is used to store the submitted tasks. According to the number of cores and the maximum number of threads above, the blocking queue will temporarily store the tasks and then take them out of the blocking queue for execution when the thread pool is idle.

A direct handshake queue, such as SynchronousQueue, acts as a pass and does not store elements itself, just like a pass, so every PUT must wait for a take. There are usually extra maximumPoolSizes threads.

Unbounded blocking queues render maximumPoolSizes meaningless. The core thread is always running in the thread pool, which ensures that every request is answered, but execution may be slow. For example, PriorityBlockingQueue and DelayQueue are unbounded blocking queues

Bounded queues and limited maximumPoolSizes prevent resource exhaustion, but for large numbers of requests, if the queue and maximum threads are full, redundant tasks may be discarded. Bounded queues are ArrayBlockingQueue and LinkedBlockingQueue

In a custom thread pool, we must set up a blocking queue for the thread pool.

Reject Tasks

When the number of threads has reached the maximum and the blocking queue is full, the submitted task will be handed to RejectedExecutionHandler for processing, such as whether to directly report error processing or directly connect to a new task. ThreadPoolExecutor provides four types of rejection processing:

  • ThreadPoolExecutor CallerRunsPolicy: this provides a simple feedback control mechanism, to slow the progress of the submit a new task;

  • ThreadPoolExecutor. AbortPolicy: the default strategy, throw a runtime exception RejectedExecutionException directly

  • Discarding the task ThreadPoolExecutor. DiscardPolicy: directly

  • ThreadPoolExecutor. DiscardOldestPolicy discarding the blocking queue in front of the task, and then try again.

ThreadFactory a ThreadFactory

The ThreadFactory is used to create new threads in the thread pool. You can define Settings such as thread name, priority, and so on for the new thread by inheriting the ThreadFactory. If not set, the default Executors. DefaultThreadFactory thread factory.

Thread pool shutdown

Stop, destroy, suspend, and resume are obsolete methods. The thread provides the interrupt() method to set the thread’s interrupt flag to true, but it does not automatically terminate the thread:

  1. If the thread is in the interrupted () state, then the thread run method internally determines whether to terminate the thread by using the isInterrupted() method. If the thread is not interrupted, then even the interrupt() method has no effect on the thread.

  2. If the thread is blocked, it exits the block and throws InterruptedException, which the blocked thread can do by catching InterruptedException.

So calling the interrupt() method on a thread, whether or not the thread can be turned off depends entirely on whether or not the thread handles the interrupt flag.

Thread pool shutdown Condition: 1. The program does not reference the thread pool, 2. There are two methods for shutting down a thread pool: shutdownNow and shutdown **shutdown:** denies the thread pool from receiving new tasks. It does not interrupt() the thread. ShutdownNow: Refuse to accept new tasks from the thread pool and call interrupt() on all threads in the thread pool. All blocked threads will simply exit execution as a return value.

Proper thread pool configuration

How many threads should be configured for the thread pool? How many threads a configuration can execute is determined by the number of cores in the CPU, which can be obtained by code:

Runtime.getRuntime().availableProcessors()

  • If the task is computationally intensive and CPU utilization is high, we can configure the maximum number of cores of CPU for the thread pool, which reduces context switching between threads.

  • If the task is IO intensive and the request will block for a large amount of time waiting for the result to return, then we can set as many threads as possible and perform a thread switch to perform other tasks. The proper value is number of CPU cores x 2

Thread pool summary:

  • Custom thread pool ThreadPoolExecutor ratio Settings: number of cores and maximum number of threads, non-core thread lifetime, blocking queue.

There are three types of blocking queues: transitionary, bounded, and unbounded. The blocking queue is set up to determine the type of business ahead of time, because there may be no response or unlimited requests. Thread factory and rejection policy: This is not necessary. In the rejection policy,ThreadPoolExecutor provides four types of rejection handling: AbortPolicy: throw exceptions directly, DiscardPolicy: discard tasks directly, DiscardOldestPolicy: discard header tasks, CallerRunsPolicy: slow submission.

  • There are shutdownNow and shutdown methods for a thread pool, both of which prevent the pool from receiving new tasks, and shutdownNow also calls the interrupt() method for each thread.

  • Thread count configuration: If the task is computationally intensive, reduce the context switch and set to CPU cores. If the task is IO intensive, switch other threads as much as possible to reduce the wait time, and set the number of CPU cores to double