1.1. What is a thread pool

A thread pool, as its name implies, is a pool that holds threads. Its purpose is to help us manage threads repeatedly, avoid the overhead of creating a large number of threads, improve response time.

1.2. Why use thread pools

As a serious siege lion, we don’t want people to look at our code and start making fun of it. New Thread().start() makes our code look cluttered, bloated, and difficult to manage and maintain, so we need to use Thread pools.

Threads are often used in programming to process tasks asynchronously, but there is some overhead associated with the creation and destruction of each thread. If you need to start a new thread each time you execute a task, the creation and destruction of these threads will consume a lot of resources. And threads are “self-contained,” which makes it hard to control, let alone a bunch of threads executing. What thread pools do for us is they save the threads for us to use when we need them, eliminating the need for repeated creation and destruction.

1.3. Thread pool processing logic

1.3.1 ThreadPoolExecutor constructor

Public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, Public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, Public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)Copy the code

Although many parameters, just look scary, in fact, very easy to understand, the following will be solved.

Let’s take the most parameters:

1.3.1.1 corePoolSize -> Maximum number of core threads in this thread pool

Core thread: After the thread pool is created, the core thread is not created but is created after the task is received. It will always exist in the thread pool (even if the thread is doing nothing), and when there is a task to be executed, if the core thread is not occupied, the core thread will be used to execute the task first. Generally, set the number to twice the number of CPU cores.

1.3.1.2 maximumPoolSize -> Maximum number of threads in this thread pool

Total threads = number of core threads + number of non-core threads

Non-core threads: Simple to understand, that is, the core threads are occupied, but there are tasks to do, create a non-core thread

1.3.1.3 keepAliveTime -> Idle timeout duration of non-core threads

This parameter can be interpreted as: there are fewer tasks, but there are many threads in the pool. Non-core threads cannot be fed for nothing. After this time, non-core threads will be killed, but core threads will remain.

1.3.1.4 TimeUnit -> keepAliveTime unit

TimeUnit is an enumeration type that includes: MICROSECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 SECONDS: SECONDS MINUTES: It’s HOURS and DAYS

1.3.1.5 BlockingQueue workQueue -> Task queue in thread pool

By default, the task come in after the first assigned to the core thread execution, core thread if all occupied, do not immediately open non-core threads to execute tasks, but will insert task queue waiting for task execution, core thread from the task queue tasks to perform, task queue can set the maximum, when insert task enough, peak, Non-core threads are created to perform tasks.

There are four common workqueues:

SynchronousQueue: When a task is received on the SynchronousQueue, it is submitted directly to the thread instead of being retained. What if all threads are working? Create a new thread to handle the task! Therefore, maximumPoolSize is usually specified as integer. MAX_VALUE, or infinite, when using this type of queue to ensure that no new thread can be created when the number of threads reaches maximumPoolSize

2.LinkedBlockingQueue: When this queue receives a task, if the number of core threads that have been created is less than the maximum number of core threads in the thread pool, a new thread (core thread) will process the task. If the number of created core threads is equal to the maximum number of core threads, the queue is queued. Since there is no maximum limit on this queue, any task that exceeds the number of core threads will be added to the queue, which invalidates maximumPoolSize because the total number of threads will never exceed corePoolSize

3. The ArrayBlockingQueue: If the queue is full, a new thread (non-core thread) will execute the task. If the queue is full, a new thread (non-core thread) will execute the task. If the total number of threads reaches maximumPoolSize, a new thread will execute the task. And the queue is full, an error occurs, or an implementation-defined saturation policy is executed

4.DelayQueue: The elements in the queue must implement the Delayed interface, which means that the task you sent in must implement the Delayed interface first. When the queue receives a task, it joins the queue first. The task will be executed only after the specified delay time is reached

1.3.1.6, ThreadFactory ThreadFactory -> create ThreadFactory

You can use a thread factory to give each created thread a name. Generally, you do not need to set this parameter.

1.3.1.6.1 Three ways to create a ThreadFactory set the thread name
CustomizableThreadFactory 1.3.1.6.1.1, the first one

The Spring framework provides CustomizableThreadFactory.

ThreadFactory springThreadFactory = new CustomizableThreadFactory("springThread-pool-");
Copy the code
1.3.1.6.1.2. Second ThreadFactoryBuilder

ThreadFactoryBuilder, provided by the Google Guava utility class, is created using a chained method.

ThreadFactory guavaThreadFactory = new ThreadFactoryBuilder().setNameFormat("retryClient-pool-").build();
Copy the code
1.3.1.6.1.3 BasicThreadFactory 3

BasicThreadFactory provided by Apache Commons-Lang3.

ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder() .namingPattern("basicThreadFactory-").build();
Copy the code

1.3.1.7. RejectedExecutionHandler Handler -> Saturation policy

This is when the task queue and fill the thread pool, the coping strategies taken by default is AbordPolicy, unable to process a new task, and throw RejectedExecutionException anomalies. There are three other strategies, as follows. (1) CallerRunsPolicy: use the caller’s thread to process the task. This strategy provides a simple feedback control mechanism that slows down the delivery of new tasks. (2) DiscardPolicy: indicates that the task cannot be executed and is deleted. DiscardOldestPolicy: Discards the latest task in the queue and executes the current task.

Don’t be dizzy, the next picture, I believe you can be combined with the picture

1.4, ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor, it can be arranged in a given delay run the command, or execute the command on a regular basis. This class is superior to a Timer when multiple worker threads are required, or when ThreadPoolExecutor is required to have additional flexibility or functionality.

Using Timer and TimerTask has some drawbacks:

  • 1.Timer creates only one thread. Problems occur when your task takes longer than the set delay time.
  • 2. The thread created by Timer does not handle exceptions and therefore terminates as soon as an unchecked exception is thrown.

JDK 5.0 recommended ScheduledThreadPoolExecutor later. This class is part of the Executor Framework and can create multiple threads in addition to handling exceptions.

1.4.1 use of Timer and TimerTask

Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { log.e("time:"); }}, 2000, 40); //2000 indicates the delay time for executing tasks for the first time. 40 indicates the interval for executing tasks in runCopy the code

1.4.2, ScheduledThreadPoolExecutor use

CorePoolSize: To provide this extension, ThreadPoolExecutor provides protected beforeExecute and afterExecute methods, which are called before and after each task execution, providing a slice of the thread task execution.

/** * Execute periodic or scheduled tasks */ @bean (name = "scheduledExecutorService") protected scheduledExecutorService scheduledExecutorService() { return new ScheduledThreadPoolExecutor(corePoolSize, new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build()) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); Threads.printException(r, t); }}; }Copy the code

Schedule (Runnable task, long delay, TimeUnit TimeUnit) This method means to run the task after a specified delay. The problem with this approach is that there is no way to know the execution result of the task. If we want to get the result of task execution, we can pass in an instance of Callable.

Public void execute(TimerTask task) {executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); }Copy the code
Public static TimerTask recordOper(final SysOperLog operLog) {return new TimerTask() {@override public void run() {// Query the operation location remotely operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); }}; }Copy the code
AsyncManager.me().execute(AsyncFactory.recordOper(operLog));
Copy the code

Reference: juejin. Cn/post / 684490…