In fact, the concept of thread pool is not so profound. It can be simply understood as that multiple idle threads are stored in a container. When a new task needs to be executed, the idle threads are taken out of the container and returned to the container after the task is executed.
The reason for using thread pool technology is mainly because the cost of creating a new thread is relatively high, and the bottom layer of the program needs to interact with the operating system. When a large number of threads with a very short lifetime need to be created in the program, threads need to be created and destroyed frequently. The resource consumption of the system is likely to be greater than that of the business processing itself, which puts the cart before the horse (because the ultimate purpose of using multithreading is to improve the business processing capacity). In order to solve this problem as much as possible, we need to reduce the frequency of thread creation and destruction, and we need to use thread pool.
Java's thread pool implementation technology is actually very simple. In real enterprise development, 99% of the cases, you will not allow yourself to code and implement a custom thread pool. Instead, you should stand on the shoulders of giants and call the API methods officially provided by Java. The thread pool API method officially provided by Java is very simple to learn and use. Let me introduce it in detail below.
1, Create a default thread pool using Executors
We can use the two static methods provided in Executors to create thread pools, as follows:
Static method name | explain |
---|---|
static ExecutorService newCachedThreadPool() | Create a default thread pool. The maximum number of threads in the thread pool is the maximum value of int |
static newFixedThreadPool(int nThreads) | Create a thread pool that specifies the maximum number of threads |
Code implementation:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; public class MyThreadPoolDemo { public static void main(String[] args) throws InterruptedException { //Create a default thread pool. The maximum number of threads in the thread pool is the maximum value of int //ExecutorService executorService = Executors.newCachedThreadPool(); //Create a thread pool with a maximum number of threads of 5 ExecutorService executorService = Executors.newFixedThreadPool(5); ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService; //Number of threads in the print thread pool System.out.println(pool.getPoolSize()); //The number of threads in the current thread pool is 0 //Submit a task to the thread pool executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + " Yes"); }); executorService.submit(()->{ System.out.println(Thread.currentThread().getName() + " Yes"); }); System.out.println(pool.getPoolSize()); //The number of threads in the current thread pool is 2 //Close destroy thread pool executorService.shutdown(); } }
As can be seen from the above code, the default thread pool officially provided by Java does not create an idle thread when it is started. When we submit a task to the thread pool, the thread pool will start a thread to execute the task. After the task is executed, the thread will not be destroyed, but returned to the thread pool in an idle state, waiting for the execution of subsequent new tasks.
2, Creating a custom thread pool using ThreadPoolExecutor
The construction method parameters for creating ThreadPoolExecutor thread pool are as follows:
Parameter 1: number of core threads
Parameter 2: maximum number of threads
Parameter 3: maximum idle thread lifetime
Parameter 4: unit of survival time (minutes, seconds, milliseconds...)
Parameter 5: task queue
Parameter 6: create a thread factory (just use the default factory: Executors.defaultThreadFactory())
Parameter 7: task rejection policy
Task rejection policy | explain |
---|---|
ThreadPoolExecutor.AbortPolicy | Redundant tasks are discarded and a RejectedExecutionException exception is thrown (default policy). |
ThreadPoolExecutor.DiscardPolicy | Redundant tasks are discarded, but no exceptions are thrown. This is not recommended. |
ThreadPoolExecutor.DiscardOldestPolicy | Discard the longest waiting task in the queue, and then add the current task to the queue. |
ThreadPoolExecutor.CallerRunsPolicy | Call the run() method of the task to bypass the thread pool and execute directly. |
The following is a code demonstration. Here, only the code with task rejection policies of AbortPolicy and CallerRunsPolicy is demonstrated, because they are commonly used.
1 use ThreadPoolExecutor Abortpolicy task rejection policy
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo01 { public static void main(String[] args) { //The number of core threads is 1, the maximum number of thread pools is 3, the capacity of task queue is 1, and the maximum lifetime of idle threads is 20 seconds ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.AbortPolicy()) ; //Currently, 5 tasks are submitted, and the thread pool can process up to 4 tasks, //RejectedExecutionException exception will be thrown when we use AbortPolicy as the task rejection policy for(int x = 0 ; x < 5 ; x++) { threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "----> Performed the task"); }); } //Close destroy thread pool threadPoolExecutor.shutdown(); } } /* You can wrap the following code with try catch to handle exceptions threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "----> Performed task ""); }); */
2. Use ThreadPoolExecutor Callerrunspolicy task rejection policy
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ThreadPoolExecutorDemo02 { public static void main(String[] args) { //The number of core threads is 1, the maximum number of thread pools is 3, the capacity of task queue is 1, and the maximum lifetime of idle threads is 20 seconds ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS , new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy()) ; //Currently, 5 tasks are submitted, and the thread pool can process up to 4 tasks for(int x = 0 ; x < 5 ; x++) { threadPoolExecutor.submit(() -> { System.out.println(Thread.currentThread().getName() + "----> Performed the task"); }); } //Close destroy thread pool threadPoolExecutor.shutdown(); } } /* The console output results are as follows: pool-1-thread-1----> Performed the task pool-1-thread-3----> Performed the task pool-1-thread-2----> Performed the task pool-1-thread-1----> Performed the task main----> Performed the task Through the output results of the console, it is found that: The fifth task is not executed by the thread in the thread pool, but bypasses the thread pool, calls the run method and executes in the main thread. */
So far, the thread pool technology officially provided by Java has been introduced. The above is just a demonstration of simple code, which can be transformed according to specific business needs in practical work. In addition, in practice, the thread pool will not be manually coded, closed or destroyed. The life cycle of the thread pool is the same as that of the whole system.