Catalog
- Pool technology -- custom thread pool
Pool technology -- custom thread pool
1. Why use thread pools?
Pooling Technology
1.1 characteristics of pool technology:
The operation of the program, essence: occupy the resources of the system! Optimize the use of resources! =>Pool technology
Thread pool, connection pool, memory pool, object pool / /..... Create and destroy. It's a waste of resources
Pooling Technology: prepare some resources in advance. If someone wants to use them, please come to me to take them and return them to me after use.
1.2 benefits of thread pool:
- Reduce resource consumption
- Reduce resource consumption
- Easy to manage.
Core: = = thread reuse, maximum concurrent number can be controlled, thread management==
1.3 how to customize a thread pool
Remember: = = three methods, seven parameters and four rejection strategies==
2. Three methods
Three ways
In java's JDK, enough classes are provided for Executors to open the default thread pool of JDK. There are three methods that can be used to open the thread pool.
2.1. Thread pool method of single thread
ExecutorService threadPool = Executors.newSingleThreadExecutor(); //Thread pool for a single thread
The thread pool opened by this method, so the name implies that there is only one thread in the pool.
2.2. Fixed thread pool size method
ExecutorService threadPool = Executors.newFixedThreadPool(5); //Fixed thread pool size
The int type parameter passed in the method is the number of threads in the pool
2.3 method of scalable thread pool
ExecutorService threadPool = Executors.newCachedThreadPool(); //Telescopic
The thread pool created by this method is not fixed size. Threads can be created dynamically in the pool according to the needs, and it will be strong if it is strong.
2.4. The complete test code is:
package com.xgp.pool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Tool class, three methods */ public class Demo01 { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); //Thread pool for a single thread // ExecutorService threadPool = Executors.newFixedThreadPool(5); / / fixed thread pool size // ExecutorService threadPool = Executors.newCachedThreadPool(); / / scalable try{ for(int i = 0;i < 10;i++) { //Use thread pool to create threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + " OK"); }); } }catch (Exception e) { e.printStackTrace(); }finally { //Close thread pool threadPool.shutdown(); } } }
The running results of opening three lines of comments in turn are as follows:
pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK
The above run result is the result of the thread pool of a single thread: it can be seen that only one thread is executing.
pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-3 OK pool-1-thread-4 OK pool-1-thread-5 OK
The above running result is the result of the fixed thread pool: because the fixed size is 5, it can be seen that there are 5 threads executing.
pool-1-thread-1 OK pool-1-thread-3 OK pool-1-thread-2 OK pool-1-thread-4 OK pool-1-thread-5 OK pool-1-thread-7 OK pool-1-thread-9 OK pool-1-thread-10 OK pool-1-thread-8 OK pool-1-thread-6 OK
The above run result is the result of the thread pool of elastic threads: it can be seen that there are indeed 10 threads executing.
3. Why custom thread pools? Analysis of three methods to create thread pool
In a single thread pool and a fixed size thread pool, due to the limited processing threads, when a large number of requests come in, they will wait in the blocking queue, while the allowed queue length is Integet.MAX_VALUE, and the maximum value of the integer is about 2.1 billion, which will lead to JVM memory overflow.
-
In the elastic thread pool, the number of threads allowed to be created is integet.max'value, which may create a large number of threads, causing the Jvm to overflow memory.
==For the above two points, the value will be seen later in the analysis of source code. There are detailed instructions in Alibaba development manual on this point, and it is highly recommended to use custom thread pool instead of these three methods. = =
4, Seven parameters
Seven parameters
Source code analysis: we want to define our own thread pool. First, from the perspective of source code, we can see how the three existing thread pools of JDK are written.
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
From the source code of the three methods, we can see that all three thread pools are ThreadPoolExecutor objects of new. Click the source code to see.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
this() method is called here, and click it to have a look.
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
So far, we can see clearly which of the seven parameters this section wants to talk about. According to the English meaning, it is easy to explain the meaning of these seven parameters.
public ThreadPoolExecutor(int corePoolSize, //Number of core threads int maximumPoolSize, //Maximum threads long keepAliveTime, //Timeout wait TimeUnit unit, //Units waiting for timeout BlockingQueue<Runnable> workQueue, //Blocking queue ThreadFactory threadFactory, //Thread pool factory RejectedExecutionHandler handler) { //Refusal strategy
In the Alibaba development manual, it is also recommended to use ThreadPoolExecutor to create thread pools.
Here you can use a picture of doing business in a bank to illustrate these seven parameters vividly.
Here, explain the meaning of this picture:
The bank only opens two windows when there are few people, and it will not close at any time. ——Number of core threads
When the number of people is more than 2 and less than 5, the next three are waiting in the waiting area for business. ——Blocking queue
When the number of people is more than 5 and less than 8, the other three windows that are being closed need to be opened for business processing, so there are 5 windows for business processing. ——Maximum threads
Three other windows will be opened, and leaders are required to call back the employees of these three windows. ——Thread pool factory
When there are too many people, the bank can't squeeze any more, so it will shut the door and refuse to accept new services. ——Reject policy
-
When the number of banks decreases again, the other three non core windows have not been in business for a long time. In order to save costs, they will be closed again. ——Timeout waiting
Through the analysis of the above seven parameters, students can also better understand the disadvantages of the three methods of JDK and why the value of the maximum value of the integer is.
5. How to create a thread pool manually
Create connection pool manually
The descriptions of the seven parameters are also mentioned, so we can follow these seven parameters to define our own thread pool. For the thread factory, we usually use the default factory, and we can use the three methods of rejection policy through source code analysis.
Click to enter the source code of defaultHandler.
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
The new AbortPolicy(); is the rejection policy used by the three methods. Let's first follow the example of the bank and customize a thread pool. The code is as follows:
package com.xgp.pool; import java.util.concurrent.*; /** * Custom thread pool */ public class Demo02 { /* public ThreadPoolExecutor(int corePoolSize, //Number of core threads int maximumPoolSize, //Maximum threads long keepAliveTime, //Timeout wait TimeUnit unit, //Units waiting for timeout BlockingQueue<Runnable> workQueue, //Blocking queue ThreadFactory threadFactory, //Thread pool factory RejectedExecutionHandler handler) {*/ //Refusal strategy public static void main(String[] args) { ExecutorService pool = new ThreadPoolExecutor( 2, 5, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<>(3), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy() //Implementation classes that throw exceptions // new ThreadPoolExecutor.CallerRunsPolicy() / / where to go // new ThreadPoolExecutor.DiscardOldestPolicy() / / no exception will be thrown and task will be lost // new ThreadPoolExecutor.AbortPolicy() / / the attempt will compete with the first one ); try{ for(int i = 0;i < 8;i++) { //Use thread pool to create pool.execute(() -> { System.out.println(Thread.currentThread().getName() + " OK"); }); } }catch (Exception e) { e.printStackTrace(); }finally { //Close thread pool pool.shutdown(); } } }
So we finished a custom thread pool. The number of core threads is 2, the maximum number of threads is 5, the number of seconds waiting for timeout is 3s, and the length of blocking queue is 3.
6. Four rejection strategies
Four rejection strategies
By analyzing the source code, we can know that the three default rejection strategies are in the ThreadPoolExecutor class. Because this class is complex and inconvenient to find, we can use the code prompt function of IDEA, which obviously prompts four rejection strategies, that is, the annotated part in the above customized thread pool.
Open the code comments of the above custom thread pool one by one, and let's test them:
6.1 rejection strategy that can throw exceptions
new ThreadPoolExecutor.AbortPolicy() //Implementation classes that throw exceptions
The result of this rejection policy is:
pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-1 OK pool-1-thread-3 OK pool-1-thread-4 OK pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-5 OK java.util.concurrent.RejectedExecutionException: Task com.xgp.pool.Demo02$$Lambda$1/2093631819@378bf509 rejected from java.util.concurrent.ThreadPoolExecutor@5fd0d5ae[Running, pool size = 5, active threads = 4, queued tasks = 0, completed tasks = 4] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369) at com.xgp.pool.Demo02.main(Demo02.java:40)
The strategy is that when the maximum number of threads + the number of blocking queues do not meet the number of requests, the system will throw an exception to solve the problem.
6.2. Where to go and where to refuse
new ThreadPoolExecutor.CallerRunsPolicy() //Where to go
The result of this rejection policy is:
pool-1-thread-1 OK main OK main OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-3 OK pool-1-thread-4 OK pool-1-thread-5 OK
It can be seen that when the number of threads in the thread pool cannot meet the requirements, the denial policy will return the tasks that cannot be served to the main thread, that is to say, the denial policy is applicable to the situation that the original thread can solve the problem.
6.3. Strategies for rejecting tasks
new ThreadPoolExecutor.DiscardOldestPolicy() //Can't throw exception, can lose task
The result of this rejection policy is:
pool-1-thread-2 OK pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-1 OK pool-1-thread-3 OK pool-1-thread-2 OK pool-1-thread-4 OK pool-1-thread-5 OK
A total of 10 tasks are counted, but only 8 tasks are processed according to the execution situation. This rejection strategy discards all tasks that cannot be allocated by threads, which will cause data loss.
6.4. Try the rejection strategy of competition
new ThreadPoolExecutor.DiscardPolicy() //Try to compete with the first
The result of this rejection policy is:
pool-1-thread-1 OK pool-1-thread-2 OK pool-1-thread-3 OK pool-1-thread-1 OK pool-1-thread-3 OK pool-1-thread-2 OK pool-1-thread-1 OK pool-1-thread-4 OK pool-1-thread-5 OK
There are 10 tasks in total, but only 9 tasks have been processed according to the implementation, one of which has been successfully competed and one has failed. This strategy will compete with the earliest threads, similar to the preemptive short job priority algorithm in the operating system. This rejection strategy will also cause data loss.
7. How to determine the maximum number of threads
7.1 CPU intensive
CPU intensive
For computers with multi-core CPU, the CPU should be fully busy, not less than the CPU core number, and should not be written dead in the code, but should be able to automatically obtain the CPU core number of the current machine, the obtained code is as follows:
System.out.println(Runtime.getRuntime().availableProcessors());
7.2 IO intensive
IO intensive
When there are a large number of IO tasks in the system, enough threads should be reserved to handle IO tasks, because IO tasks are extremely time-consuming. If it is judged that there are 10 IO intensive tasks in the system, the number of defined threads should be greater than 10.
7.3 formula summary
Maximum threads = machine nuclides * 2 + IO intensive tasks
For the above formula, it is only a summary on the Internet, and the author has not carried out in-depth test to understand it. Readers should make reasonable adjustment according to their own business needs.