Chapter 2 ThreadPoolExecutor use and working mechanism using cutting fluid Chip Breaking Mechanism
1. The most basic thread pool ThreadPoolExecutor
Usage:
1/** 2 * ThreadPoolExecutor test Class 3 * Note: 4*1. ThreadPoolExecutor is a thread pool of 5*2. multiple tasks can be selected from the thread pool to run 6 */7 public class ThreadPoolExecutorTest {8 private static ThreadPoolExecutor executor = 9 new ThreadPoolExecutor (5, 10, 30, TimeUnit. SECONDS, new ArrayBlockingQueue <Runnable> (10); 10 11 public void executeTask () {12 Task1 task1 = new Task1 (); // build task 113 Task2 task2 = new Task2 (); // build task 214 executor. e Xecute (task1); // executes the task 115 executor.exe cute (task2 ); // execute task 216} 17 18/* 19 * Basic Task 220 */21 class Task1 implements Runnable {22 public void run () {23 // business 24 for a specific task (int I = 0; I <1000; I ++) {25 System. out. println ("hello xxx !!! "); 26} 27} 28} 29 30/* 31 * basic tasks 232 */33 class Task2 implements Runnable {34 public void run () {35 // service 36 for a specific task (int I = 0; I <5; I ++) {37 System. out. println ("hello world2 !!! "); 38} 39} 40} 41 42 public static void main (String [] args) {43 ThreadPoolExecutorTest test = new ThreadPoolExecutorTest (); 44 test.exe cuteTask (); 45} 46}View Code
Note:
In the code, a thread pool (executor) and two tasks that implement the Runnable interface (task1 and task2) are constructed and submitted to the executor for execution.
Thread Pool configuration: detailed descriptions of the working mechanism and parameters under the set.
Of course, the above execution results are crossover, because there is a thread switch.
2. Working Mechanism
A. When A new task is submitted to the execute () method of ThreadPoolExecutor, ifRunningIf the number of threads is less than corePoolSize, a new thread is created to process the task;
Note:This is a running thread in the pool. Why? It is because the Core Thread creates a thread for every task. This is part 3. After reading the third part, you will think, in fact, to put it another way: "If the thread in the current pool is less than corePoolSize", it will be more accurate, because we may create the Core Thread in advance through the method described below. If a task is created at this time and all the core threads are idle, at this time, no new threads will be created.
B. If the thread in the current pool is greater than or equal to corePoolSize but smaller than maximumPoolSize, if the queue is full, a new thread is created to process the task. If the queue is not full, add a task to the queue;
C. If the queue is full and the number of running threads is equal to maximumPoolSize, the task will be rejected (rejected)
3. parameter description
A. corePoolSize and maximumPoolSize
- If corePoolSize = maximumPoolSize, the size of the thread pool is fixed (this is similar to the heap memory, to prevent the loss caused by expansion, but it depends on the situation );
- By default, core threads is created and started only when a new task arrives, but can be changed through prestartCoreThread and prestartAllCoreThreads;
B. ThreadFactory
- You can use java. util. concurrent. ThreadFactory to create a new thread.
- If ThreadFactory is not specified, useDefaultExecutors #DefaultThreadFactory;
- Through this default thread factory, all created threads will be added to the same ThreadGroup, and these threads will have the same priority (NORM_PRIORITY) and are all non-daemon threads.
Note: There is a daemon thread concept. A typical background thread is a garbage collection thread. The difference between this thread and other application threads is: when no application thread exists, the background thread disappears automatically.
C. keepAliveTime
- If the number of threads in the pool exceeds the corePoolSize, the threads will be terminated if they are idle (idle) outside the keepAliveTime;
- This mechanism can reduce resource waste when the pool is not active;
- By default, the keep-alive mechanism is used only when the number of threads exceeds corePoolSizeThreads;
- Of course, you can also apply this keep-alive mechanism to core threads by using ThreadPoolExecutor # allowCoreThreadTimeOut (boolean) (as long as keepAliveTime> 0)
D. Queue
AnyBlockingQueueCan be used to pass and store the tasks submitted to the thread pool. There are three queue policies:
1)SynchronousQueue (default):
- Directly hand over the task to the thread instead of joining the queue. If no thread is ready to process the task submitted to the pool immediately, a new thread is created to process the task;
- This policy requires maximumPoolSizes to be unbounded to ensure that new tasks are not rejection;
- The biggest drawback of this method: when a task arrives at a speed greater than the speed at which the task is processed, the number of threads will grow.
2) unbounded blockingqueue:
- Because the queue is unbounded, when the running thread is equal to corePoolSize, the new task will be queued and will not be executed by creating a new thread (that is, the number of threads in the pool will never be greater than corePoolSize );
- Disadvantages of this method: when a task arrives at a speed greater than the speed at which the task is processed, the queue length will go crazy.
3) bounded queue ArrayBlockingQueue:
- This method is very difficult to handle. We need to consider the size of ArrayBlockingQueue and maximumPoolSize;
- When the ArrayBlockingQueue is large and maximumPoolSize is small, the CPU usage, OS resources, and context switching are reduced, but the throughput is reduced. --> This is the case with few threads;
- If the task is blocked frequently (for example, they are I/O bound), more threads are needed;
- When the ArrayBlockingQueue is small and the maximumPoolSize is large, the CPU usage will be busy, but some unacceptable scheduling will also be encountered, and the throughput will be reduced.
Note: This configuration item is troublesome and will be discussed later.
E. Reject a task
The scenario where the code is rejected: Check the working mechanism of the first part.
When a task is rejected, the execute () method calls RejectedExecutionHandler # rejectedExecution. There are four handler policies:
1) ThreadPoolExecutor. CallerRunsPolicy: calls the execute () thread to handle the task.
2) ThreadPoolExecutor. DiscardPolicy: tasks that cannot be executed are directly thrown away.
3) ThreadPoolExecutor. DiscardOldestPolicy: If executor is not disabled, tasks in the queue header will be discarded and added to the end of the team.
4) ThreadPoolExecutor.AbortPolicy (default): Reject the task and throw an exception
F. AOP
ThreadPoolExecutor provides two methods to call ThreadPoolExecutor # beforeExecute and ThreadPoolExecutor # afterExecute before and after each task is executed.
4. Apply the start instance
Thread Pool parameters built in the instance:
- CorePoolSize = 5
- MaximumPoolSize = 10
- KeepAliveTime = 30 s
- Queue: ArrayBlockingQueue, with a size of 10
- Thread factory: defathreadthreadfactory (default)
- Reject policy: AbortPolicy (default)
Set the working mechanism:
1) When <= 5 tasks are submitted concurrently to the executor (number of tasks <= corePoolSize), the executor uses five core threads to execute these tasks;
2) at this time, another task will be created. If the five core threads have Idle threads, they will be processed using Idle threads. If they are all busy, the task will enter the queue;
3) then run the task again, as in step 2, until the task is full. At this time, if another task has five core threads with Idle threads, execute the task directly. If five core threads are busy, a new thread is created to execute the task;
4) if the last five threads are busy and the queue is full, and the number of threads in the pool is already 10 (the total number of threads in the pool = maximumPoolSize). At this time, the reject policy is executed. Here, the default AbortPolicy is used, this means that the task is abandoned directly and an exception is thrown.
During code execution, if the five threads created later have not been called for more than 30 seconds, the thread will be recycled.
Q: (I will answer this question after reading the source code of ThreadPoolExecutor)
When the queue is full, a task is generated, and one of the five core threads is idle, which of the following two conditions is correct:
1) The idle Core Thread executes the task just arrived.
2) The idle Core Thread directly executes the tasks in the queue header and puts the tasks that have just arrived at the end of the team.
Answer: The answer to this question is one sentence. If there is a idle Core Thread, the Core Thread is used to execute the task. If there is no idle Core Thread, the task will be queued. So select 1)
Finally, the source code resolution addresses of the two queues mentioned above are listed:
ArrayBlockingQueue: Chapter 8 ArrayBlockingQueue source code analysis
LinkedBlockingQueue: Chapter 9 LinkedBlockingQueue source code analysis