Pool technology -- custom thread pool

Keywords: Java Windows JDK less

Catalog

Pool technology -- custom thread pool

1. Why use thread pools?

Pooling Technology

1.1 characteristics of pool technology:

  1. The operation of the program, essence: occupy the resources of the system! Optimize the use of resources! =>Pool technology

  2. Thread pool, connection pool, memory pool, object pool / /..... Create and destroy. It's a waste of resources

  3. 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:

  1. Reduce resource consumption
  2. Reduce resource consumption
  3. 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

  1. 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.

  2. 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:

  1. The bank only opens two windows when there are few people, and it will not close at any time. ——Number of core threads

  2. 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

  3. 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

  4. Three other windows will be opened, and leaders are required to call back the employees of these three windows. ——Thread pool factory

  5. 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

  6. 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.

Posted by Justin98TransAm on Sat, 22 Feb 2020 19:42:40 -0800