ThreadPool thread pool

ThreadPool thread pool

Why use thread pool

Example

Ten years ago, single-core CPU computers had fake multi-threading, like a circus clown playing with multiple balls, and the CPU needed to switch back and forth. Nowadays, it is a multi-core computer. Multiple threads run on independent CPUs, which is more efficient without switching.

Advantages of thread pool

The only thing the thread pool does is to control the number of running threads, put tasks into the queue during processing, and then start these tasks after the threads are created. If the number of threads exceeds the maximum number, the excess threads will queue up and wait for other threads to execute. After completion, take the task out of the queue and execute it.

Main features

  1. Thread reuse: Reduce resource consumption. Reduce the overhead caused by thread creation and destruction by reusing already created threads
  2. Control the maximum number of concurrencies: improve response speed. When a task arrives, the task can be executed immediately without waiting for thread creation.
  3. Manage threads: Improve thread manageability. Threads are scarce resources. If they are created without restrictions, they will not only consume system resources, but also reduce the stability of the system. The thread pool can be used for unified allocation, tuning and monitoring.

How to use thread pool

Architecture Description

The thread pool in Java is implemented through the Executor framework, which uses the classes Executor, Executors, ExecutorService, and ThreadPoolExecutor.

Coding implementation

  • Executors.newFixedThreadPool(5): It has good performance in executing long-term tasks. It creates a thread pool with N fixed threads and a fixed number of threads.

    • Create a thread pool with five threads. Similar to a bank with five acceptance windows

      ExecutorService threadPool = Executors.newFixedThreadPool(5);
      
    • The source code is as follows. The corePoolSize and maximumPoolSize values of the thread pool created by newFixedThreadPool are equal, and it uses LinkedBlockingQueue.

  • Executors.newSingleThreadExecutor(): execution of one task, one pool and one thread

    • Create a thread pool with one thread. Similar to a bank having one acceptance window

      ExecutorService threadPool = Executors.newSingleThreadExecutor();
      
    • The source code is as follows. The thread pool corePoolSize and maximumPoolSize values created by newSingleThreadExecutor are both 1, and it uses LinkedBlockingQueue

  • Executors.newCachedThreadPool(): Execute many short-term asynchronous tasks, the thread pool creates new threads as needed, but will reuse previously built threads when they are available. Expandable, strong when strong

    • A thread pool has N threads. Similar to a bank having N acceptance windows

      ExecutorService threadPool = Executors.newCachedThreadPool();
      
    • The source code is as follows; the thread pool created by newCachedThreadPool sets corePoolSize to 0 and maximumPoolSize to Integer.MAX_VALUE. It uses SynchronousQueue, which means that when a task comes, a thread is created to run. When the thread is idle for more than 60 seconds, the thread is destroyed.

       The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

Code

package com.andy.blog.test.juc;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Created by andy on 2022-01-01 13:07
 */
public class MyThreadPoolDemo {<!-- -->

    public static void main(String[] args) {<!-- -->

        //Create a thread pool with five threads. Similar to a bank with five acceptance windows
        //ExecutorService threadPool = Executors.newFixedThreadPool(5);
        //A thread pool has one thread. Similar to a bank having one acceptance window
        //ExecutorService threadPool = Executors.newSingleThreadExecutor();
        //A thread pool has N threads. Similar to a bank having N acceptance windows
        ExecutorService threadPool = Executors.newCachedThreadPool();

        try {<!-- -->
            //Simulate that 10 customers come to Chestnut Bank for business. Currently, there are 5 staff in the pool to provide services.
            for (int i = 1; i <= 10; i + + ) {<!-- -->
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "\tProcessing business"));
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            threadPool.shutdown();
        }

    }
}

Several important parameters of the thread pool

Seven parameters

 The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

  1. corePoolSize: the number of resident core threads in the thread pool
  2. maximumPoolSize: The maximum number of threads that can be executed simultaneously in the thread pool. This value must be greater than or equal to 1
  3. keepAliveTime: The survival time of excess idle threads. When the number of threads in the current pool exceeds corePoolSize, when the idle time reaches keepAliveTime, the excess threads will be destroyed until only corePoolSize threads remain.
  4. unit: unit of keepAliveTime
  5. workQueue: task queue, tasks that have been submitted but have not yet been executed
  6. threadFactory: Represents the thread factory that generates worker threads in the thread pool. It is used to create threads. Generally, the default is enough.
  7. handler: Rejection strategy, indicating how to reject the runnable request execution strategy when the queue is full and the worker thread is greater than or equal to the maximum number of threads in the thread pool (maximumPoolSize)

The underlying working principle of the thread pool

 The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

Examples

As shown in the figure above, the largest box on the right is a bank outlet (i.e., thread pool, the maximum number of threads in this thread pool is 5). corePoolSize represents today’s duty window, that is, two windows (threads). First, the bank Two people, 1 and 2, came to the branch, and the window was just right. At this time, 1 and 2 were handling business at the window, but then three users, 3, 4, and 5, came to handle the business and found that the windows were all occupied. At this time, they needed to wait. Waiting in the guest area (blockingQueue blocking); then three more users, 6, 7, and 8, came and found that the waiting area was full and the window was also full, so they were very angry and called the lobby manager, who temporarily called the employees After working overtime, three temporary windows were opened (expanded to the maximum number of threads in the thread pool, maximumPoolSize) for users 3, 4, and 5 to handle. At this time, users 6, 7, and 8 went to the waiting area to wait, but they came again. Two users, 9 and 10, have been invited to handle the business, but the maximum window of the outlet can only be 5. At this time, users need to be refused (handler) to handle the business, and the two users 9 and 10 are rejected and asked to go to other outlets. Management, maximumPoolSize includes corePoolSize, and then the three users 3, 4, and 5 complete the transaction. But after 2 hours (assuming keepAliveTime is set to 2 hours) no user comes to handle it. At this time, the temporary window needs to be closed and not opened. This is the principle of the thread pool.

Schematic diagram

 The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

Schematic diagram explanation

  1. After creating the thread pool, start waiting for requests.
  2. When calling the execute() method to add a request task, the thread pool will make the following judgment:
    1. If the number of running threads is less than corePoolSize, create a thread immediately to run the task;
    2. If the number of running threads is greater than or equal to corePoolSize, then put this task into the queue;
    3. If the queue is full at this time and the number of running threads is less than maximumPoolSize, then a non-core thread must be created to run the task immediately;
    4. If the queue is full and the number of running threads is greater than or equal to maximumPoolSize, the thread pool will initiate a saturation rejection policy to execute.
  3. When a thread completes a task, it takes a task from the queue and executes it.
  4. When a thread has nothing to do for more than a certain period of time (keepAliveTime), the thread will judge:
    1. If the number of currently running threads is greater than corePoolSize, then this thread is stopped.
    2. So after all tasks of the thread pool are completed, it will eventually shrink to the size of corePoolSize.

Which thread pool to use? How to set reasonable parameters during production

Thread pool rejection policy

What is it

The waiting queue is full and no new tasks can be filled. At the same time, the max thread in the thread pool has also been reached and cannot continue to serve new tasks. This is when we need to reject the policy mechanism to deal with this problem reasonably.

JDK built-in rejection policy
AbortPolicy (default)

Throw RejectedExecutionException directly to prevent the system from running normally

public class MyThreadPoolDemo {<!-- -->

    public static void main(String[] args) {<!-- -->

        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
        try {<!-- -->
            //Simulate that 9 customers come to Chestnut Bank for business. Currently, there are 5 staff in the pool to provide services.
            for (int i = 1; i <= 9; i + + ) {<!-- -->
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "\tProcessing business"));
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            threadPool.shutdown();
        }
    }
}

The above code can accommodate up to 8 threads, so the code execution result is: throwing RejectedExecutionException exception

pool-1-thread-1 handles business
pool-1-thread-3 handles business
pool-1-thread-3 handles business
pool-1-thread-3 handles business
pool-1-thread-2 handles business
pool-1-thread-5 handle business
pool-1-thread-4 handles business
pool-1-thread-1 handles business
java.util.concurrent.RejectedExecutionException: Task com.andy.blog.test.juc.MyThreadPoolDemo$$Lambda$1/1146743572@7a4f0f29 rejected from java.util.concurrent.ThreadPoolExecutor@45283ce2[Running, pool size = 5, active threads = 5, queued tasks = 2, completed tasks = 1]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.andy.blog.test.juc.MyThreadPoolDemo.main(MyThreadPoolDemo.java:27)
CallerRunsPolicy

“Caller Run” is a tuning mechanism that neither abandons tasks nor throws exceptions, but rolls back certain tasks to the caller, thereby reducing the traffic of new tasks.

public class MyThreadPoolDemo {<!-- -->

    public static void main(String[] args) {<!-- -->

        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());
        try {<!-- -->
            //Simulate that 10 customers come to Chestnut Bank for business. Currently, there are 5 staff in the pool to provide services.
            for (int i = 1; i <= 10; i + + ) {<!-- -->
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "\tProcessing business"));
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            threadPool.shutdown();
        }
    }
}

The above code can accommodate up to 8 threads, but it uses the CallerRunsPolicy rejection policy. It will not abandon the task or throw an exception, but will roll back the task to the caller, which is the current main thread, so the code execution result is :

pool-1-thread-2 handles business
pool-1-thread-1 handles business
pool-1-thread-2 handles business
pool-1-thread-2 handles business
main handle business
pool-1-thread-3 handles business
pool-1-thread-3 handles business
pool-1-thread-5 handle business
pool-1-thread-4 handles business
pool-1-thread-1 handles business
DiscardOldestPolicy

Abandon the longest waiting task in the queue, then add the current task to the queue and try to submit the current task again.

public class MyThreadPoolDemo {<!-- -->

    public static void main(String[] args) {<!-- -->

        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy());

        try {<!-- -->
            //Simulate that 10 customers come to Chestnut Bank for business. Currently, there are 5 staff in the pool to provide services.
            for (int i = 1; i <= 10; i + + ) {<!-- -->
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "\tProcessing business"));
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            threadPool.shutdown();
        }
    }
}

The above code can accommodate up to 8 threads, but using the DiscardOldestPolicy rejection strategy, it will discard the longest waiting task in the queue, so the code execution result is: 8

pool-1-thread-2 handles business
pool-1-thread-2 handles business
pool-1-thread-2 handles business
pool-1-thread-2 handles business
pool-1-thread-1 handles business
pool-1-thread-4 handles business
pool-1-thread-3 handles business
pool-1-thread-5 handle business
DiscardPolicy

This strategy silently discards unprocessable tasks without any processing or throwing an exception. This is the best strategy if tasks are allowed to be lost.

public class MyThreadPoolDemo {<!-- -->

    public static void main(String[] args) {<!-- -->

        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardPolicy());

        try {<!-- -->
            //Simulate that 10 customers come to Chestnut Bank for business. Currently, there are 5 staff in the pool to provide services.
            for (int i = 1; i <= 10; i + + ) {<!-- -->
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "\tProcessing business"));
            }
        } catch (Exception e) {<!-- -->
            e.printStackTrace();
        } finally {<!-- -->
            threadPool.shutdown();
        }
    }
}

The above code can accommodate up to 8 threads, but the DiscardPolicy rejection strategy is used, so the code execution result is: 8

pool-1-thread-1 handles business
pool-1-thread-1 handles business
pool-1-thread-1 handles business
pool-1-thread-1 handles business
pool-1-thread-3 handles business
pool-1-thread-5 handle business
pool-1-thread-4 handles business
pool-1-thread-2 handles business

The above built-in rejection strategies all implement the RejectedExecutionHandle interface

In work, which of the three methods of creating a thread pool, single/fixed/variable, is used more often? Super big pit

  • The answer is none, we can only use customized ones in our work

  • The JDK in Executors has already provided it for you, why not use it?

     The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

How to set maximumPoolSize?

There are two types, the thread pool is based on CPU-intensive work or IO-intensive work.

  • CPU-intensive work: maximumPoolSize = number of cpu cores + 1;
  • IO intensive work: number of cpu cores * 2

Check the number of computer cpu cores

Method 1:

Right-click on Computer->Device Manager->Processor (as shown below, the number of processors indicates the number of cores)

 The external link image transfer failed. The source site may have an anti-leeching mechanism. It is recommended to save the image and upload it directly

Method 2:

Use code

System.out.println(Runtime.getRuntime().availableProcessors());

/**
 *6
 */

Related article: Setting the maximum number of thread pool cores