Explain the role of thread pool and how to use thread pool in Java

Server-side applications (such as databases and Web servers) need to process high-concurrency and short-consuming requests from clients. Therefore, frequent creation of threads required to process these requests is a very resource-consuming operation. The conventional method is to create a new thread for a new request. Although this method seems easy to implement, it has major disadvantages. Creating a new thread for each request will take more time and more system resources when creating and destroying threads. Therefore, a JVM that creates too many threads at the same time may cause insufficient system memory. This requires limiting the number of threads to be created, that is, the thread pool needs to be used.

1. What is a thread pool in Java?

Thread pool technology is a thread reuse technology, using previously created threads to perform current tasks, and provides solutions to thread cycle overhead and resource conflicts. Because the thread already exists when the request arrives, the delay caused by the thread creation process is eliminated, and the application can respond faster.

  • Java provides an executor framework centered on the Executor interface and its sub-interfaces ExecutorService and ThreadPoolExecutor . By using Executor, you only need to implement the Runnable interface to complete the thread task and give it to the executor for execution.
  • Encapsulate the thread pool for you, and focus your programming tasks on the realization of specific tasks, rather than the implementation mechanism of threads.
  • To use the thread pool, we first create an ExecutorService object, and then pass a set of tasks to it. The ThreadPoolExcutor class can set the thread pool initialization and maximum thread capacity.

The above figure shows that the thread pool initialization has 3 threads, and there are 5 task objects to be run in the task queue.

Executor thread pool method |Method|Description| |---|---| |newFixedThreadPool(int) | Create a thread pool with a fixed number of threads, the int parameter indicates the number of threads in the thread pool | |newCachedThreadPool() | Create A cacheable thread pool, which can flexibly reclaim idle threads. If there is no idle thread, create a new thread to process the task. | |newSingleThreadExecutor() | Create a single-threaded thread pool, it will only use a single worker thread to perform tasks | |newScheduledThreadPool|Create a fixed-length thread pool, support timing and periodic task execution|

In the case of a fixed thread pool, if the executor currently runs all threads, the pending tasks will be placed in the queue and executed when the threads become idle.

Two, thread pool example

In the following content, we will introduce the executor of the thread pool.

Steps to follow to create a thread pool to handle tasks

  1. Create a task object (implement the Runnable interface) to execute specific task logic
  2. Use Executors to create a thread pool ExecutorService
  3. Hand over the task object to be executed to ExecutorService for task processing
  4. Stop the Executor thread pool
//第一步: 创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑 (Step 1) 
class Task implements Runnable  {
    private String name;

    public Task(String s) {
        name = s;
    }

    // 打印任务名称并Sleep 1秒
    // 整个处理流程执行5次
    public void run() {
        try{
            for (int i = 0; i<=5; i++) {
                if (i==0) {
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("任务初始化" + name +" = " + ft.format(d));
                    //第一次执行的时候,打印每一个任务的名称及初始化的时间
                }
                else{
                    Date d = new Date();
                    SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
                    System.out.println("任务正在执行" + name +" = " + ft.format(d));
                    // 打印每一个任务处理的执行时间
                }
                Thread.sleep(1000);
            }
            System.out.println("任务执行完成" + name);
        }  catch(InterruptedException e)  {
            e.printStackTrace();
        }
    }
}

Test case

public class ThreadPoolTest {
    // 线程池里面最大线程数量
    static final int MAX_SIZE = 3;

    public static void main (String[] args) {
        // 创建5个任务
        Runnable r1 = new Task("task 1");
        Runnable r2 = new Task("task 2");
        Runnable r3 = new Task("task 3");
        Runnable r4 = new Task("task 4");
        Runnable r5 = new Task("task 5");

        // 第二步:创建一个固定线程数量的线程池,线程数为MAX_SIZE
        ExecutorService pool = Executors.newFixedThreadPool(MAX_SIZE);

        // 第三步:将待执行的任务对象交给ExecutorService进行任务处理
        pool.execute(r1);
        pool.execute(r2);
        pool.execute(r3);
        pool.execute(r4);
        pool.execute(r5);

        // 第四步:关闭线程池
        pool.shutdown();
    }
} 

Example execution result

任务初始化task 1 = 05:25:55
任务初始化task 2 = 05:25:55
任务初始化task 3 = 05:25:55
任务正在执行task 3 = 05:25:56
任务正在执行task 1 = 05:25:56
任务正在执行task 2 = 05:25:56
任务正在执行task 1 = 05:25:57
任务正在执行task 3 = 05:25:57
任务正在执行task 2 = 05:25:57
任务正在执行task 3 = 05:25:58
任务正在执行task 1 = 05:25:58
任务正在执行task 2 = 05:25:58
任务正在执行task 2 = 05:25:59
任务正在执行task 3 = 05:25:59
任务正在执行task 1 = 05:25:59
任务正在执行task 1 = 05:26:00
任务正在执行task 2 = 05:26:00
任务正在执行task 3 = 05:26:00
任务执行完成task 3
任务执行完成task 2
任务执行完成task 1
任务初始化task 5 = 05:26:01
任务初始化task 4 = 05:26:01
任务正在执行task 4 = 05:26:02
任务正在执行task 5 = 05:26:02
任务正在执行task 4 = 05:26:03
任务正在执行task 5 = 05:26:03
任务正在执行task 5 = 05:26:04
任务正在执行task 4 = 05:26:04
任务正在执行task 4 = 05:26:05
任务正在执行task 5 = 05:26:05
任务正在执行task 4 = 05:26:06
任务正在执行task 5 = 05:26:06
任务执行完成task 4
任务执行完成task 5

As shown in the program execution result, task 4 or task 5 is executed only when the thread in the pool becomes idle. Before that, additional tasks will be placed in the queue to be executed.

The thread pool performs the first three tasks, and the threads in the thread pool are reclaimed and vacated before processing tasks 4 and 5

One of the main advantages of using this thread pool method is that if you want to process 10,000 requests at a time, but do not want to create 10,000 threads, so as to avoid downtime caused by excessive use of system resources. You can use this method to create a thread pool containing 500 threads, and you can submit 500 requests to the thread pool. ThreadPool will create up to 500 threads at this time, processing 500 requests at a time. After the process of any thread is completed, ThreadPool will internally allocate the 501st request to that thread, and will continue to perform the same operation on all remaining requests. In the case of relatively tight system resources, the thread pool is an effective solution to ensure the stable operation of the program.

Three, the use of thread pool considerations and tuning

  1. Deadlock: Although deadlock may occur in any multithreaded program, the thread pool introduces another deadlock case, in which all execution threads are waiting for the execution result of a blocked thread in the queue, causing the thread to be unable to continue execution.
  2. Thread leakage: If the thread in the thread pool does not return correctly when the task is completed, thread leakage will occur. For example, if a thread throws an exception and the pool class does not catch this exception, the thread will exit abnormally, and the size of the thread pool will be reduced by one. If this situation is repeated multiple times, the thread pool will eventually become empty and no threads can be used to perform other tasks.
  3. Frequent thread rotation: If the thread pool size is very large, context switching between threads will waste a lot of time. So in the case of system resources permitting, it is not that the larger the thread pool, the better.

Thread pool size optimization : The optimal size of the thread pool depends on the number of processors available and the nature of the tasks to be processed. For CPU-intensive tasks, assuming that the system has N logical processing cores, a maximum thread pool size of N or N+1 will achieve maximum efficiency. For I/O-intensive tasks, you need to consider the ratio of the request waiting time (W) to the service processing time (S). The maximum thread pool size is N*(1+ W/S) to achieve the highest efficiency.

Do not use the above summary dogmatically. You need to set up and tune flexibly according to your application task processing type, and test experiments are indispensable.

Welcome to follow my blog, there are many boutique collections

  • This article is reproduced indicate the source (en must not turn only the text): letters Gebo off .

If you think it is helpful to you, please like and share it for me! Your support is my inexhaustible creative motivation! . In addition, the author has output the following high-quality content recently, and I look forward to your attention.

Guess you like

Origin blog.csdn.net/hanxiaotongtong/article/details/112598976