Article directory:
Demo1 (create thread pool using Executors)
Demo2 (create thread pool using ThreadPoolExecutor)
About the seven parameters and four rejection strategies in ThreadPoolExecutor
Execution strategy of thread pool
Write in front
You can use new thread (() - > {task executed by thread}) start(); This form starts a thread. When the run() method ends, the thread object will be released by GC.
In a real production environment, many threads may be required to support the whole application. When the number of threads is very large, it will exhaust CPU resources. If you do not control and manage threads, it will affect the performance of the program. Thread creation and start-up costs; Thread destruction overhead; The overhead of thread scheduling; The number of threads is limited by the number of CPU processors. Thread pool is a common way to use threads effectively. A certain number of working threads can be created in the thread pool in advance. The client code directly submits the tasks to the thread pool as an object. The thread pool caches these tasks in the work queue. The working threads in the thread pool constantly take out the tasks from the queue and execute them.
JDK provides a set of Executor framework, which can help developers use thread pool effectively.
Demo1 (create thread pool using Executors)
package com.szh.threadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * Basic use of thread pool */ public class Test01 { public static void main(String[] args) { //Create a thread pool with 5 thread sizes ExecutorService executorService= Executors.newFixedThreadPool(5); //Submit 13 tasks to the thread pool, which are stored in the blocking queue of the thread pool, //The five threads in the thread pool take tasks from the blocking queue for execution for (int i = 0; i < 13; i++) { executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getId() + " Thread number is executing task, start time:" + System.currentTimeMillis()); try { TimeUnit.MILLISECONDS.sleep(1000 * 3); //Simulation task execution duration } catch (InterruptedException e) { e.printStackTrace(); } } }); } //Close thread pool executorService.shutdown(); } }
Here, a thread pool with a fixed size of 5 is created, and 13 tasks are submitted to the thread pool at the same time. Then these 5 threads in the thread pool will fetch tasks from the blocking queue in the thread pool for execution every time.
Each time these five sub threads are executed together, so their start time can be seen to be the same.
Demo2 (create thread pool using ThreadPoolExecutor)
package com.szh.threadpool; import java.util.concurrent.*; /** * */ public class Test03 { public static void main(String[] args) { //Explain these seven parameters with an example of a bank ExecutorService threadPool=new ThreadPoolExecutor( 3, //Specify the number of core threads in the thread pool (two normally open business windows) 5, //Specifies the maximum number of threads in the thread pool (five windows in total) 2, //When the number of threads in the thread pool exceeds the corePoolSize, the survival time of the redundant idle threads, that is, how long the idle threads will be destroyed TimeUnit.SECONDS, //Duration unit of keepAliveTime new LinkedBlockingQueue<>(3), //Task queue: submit the task to the task queue for execution (the size of the bank waiting area) Executors.defaultThreadFactory(), //Default route pool factory new ThreadPoolExecutor.AbortPolicy()); //Reject strategy: how to reject when there are too many tasks to handle /* By default, AbortPolicy() will throw an exception CallerRunsPolicy() As long as the thread pool is not closed, the currently discarded task will run in the caller thread DiscardPolicy() Discard this unprocessed task directly DiscardOldestPolicy() Discard the oldest task in the task queue and try to submit a new task again */ //Submit 9 tasks to the thread pool for (int i = 0; i < 9; i++) { threadPool.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }); } //Close connection threadPool.shutdown(); } }
When defining the maximum size of the thread pool, there are generally two strategies: CPU intensive and IO intensive. The so-called CPU intensive is defined as several cores of CPU, and mine is eight cores, so it is defined as 8. Runtime.getRuntime().availableProcessors();// Gets the number of cores of the CPU.
IO intensive is to judge how many programs consume IO threads in the program. The maximum thread pool size should be greater than this value.
In the above case, the maximum number of thread pools I set is 5, and the maximum number of blocking queues is 3. The total is 8. In other words, it can hold up to 8 tasks. In the for loop, I submitted 9 tasks to the thread pool. From the running results, it can be seen that the first 8 tasks can be executed normally. When the 9th task is executed, because the number of core thread pools in the thread pool is 3, 9 tasks have obviously exceeded, so 3 tasks will be handed over to the core thread for execution, 9-3 = 6, and the remaining 6 will be stored in the blocking queue; Then the thread pool will judge that the blocking queue indicator is full. I set the maximum blocking queue to 3. At this time, the blocking queue can only accommodate 3 at most, so the task balance is 6-3 = 3. The thread pool will ask for its maximum number of thread pools for the other 3 tasks. Here I set it to 5, because the number of core thread pools has occupied 3 before, In other words, at this time, the maximum number of thread pools is 5-3 = 2, so the thread pool can only bear two more tasks at most. However, 2 < 3, so there is still one task that the thread pool cannot handle. At this time, the reject policy will be executed. I set the default reject policy here, and AbortPolicy will throw an exception directly.
About the seven parameters and four rejection strategies in ThreadPoolExecutor
Seven parameters.
int corePoolSize //Number of core thread pools int maximumPoolSize //Maximum number of thread pools long keepAliveTime //Timeout survival time TimeUnit unit //Timeout unit BlockingQueue<Runnable> workQueue //Blocking queue ThreadFactory threadFactory //Thread factory for creating threads RejectedExecutionHandler handler //Reject strategy
The four rejection strategies can be seen from the source code of ThreadPoolExecutor. They are actually the four static internal classes of ThreadPoolExecutor.
- AbortPolicy policy policy, an exception will be thrown
- CallerRunsPolicy policy: as long as the thread pool is not closed, the currently discarded tasks will be run in the caller thread
- DiscardOldestPolicy policy: discard the oldest task in the task queue and try to submit a new task again
- Discard policy policy, which directly discards this unprocessable task
Execution strategy of thread pool
1. When the thread pool was first created, there was no thread in it. The task queue is passed in as a parameter. However, even if there are tasks in the queue, the thread pool will not execute them immediately.
2. When calling the execute() method to add a task, the thread pool will make the following judgment:
(1) If the number of running threads is less than the number of core threads, create a thread to run the task immediately;
(2) If the number of running threads is greater than or equal to the number of core threads, add the task to the blocking queue;
(3) If the blocking queue is full and the number of running threads is less than the maximum poolsize (the maximum number of threads), create a thread to run the task;
(4) If the queue is full and the number of running threads is greater than or equal to the maximumPoolSize (the maximum number of threads), the thread pool will execute the reject policy (four, AbortPolicy throws an exception directly by default) and tell the caller "I can't accept tasks anymore".
(5) When a thread completes a task, it will remove a task from the queue for execution.
(6) If the number of threads running in the pool is greater than the current one, the number of threads will be stopped. If the number of threads running in the pool is greater than the current one, the pool will be stopped. Therefore, after all tasks of the thread pool are completed, it will eventually shrink to the size of corePoolSize.