Thread Pool principle parsing and thread pool Parsing
Note: the source code of this article is derived from JDK1.6.
1. Content contained in the thread pool
2. Data Structure of the thread pool [core class ThreadPoolExecutor ]:Worker: Work class. A worker indicates that a thread is started.
Execute workQueue cyclicallyWorkQueue of all tasks in it: the task queue, which is used to store the keepAliveTime of the tasks to be executed: the thread activity holding time. The worker thread in the thread pool remains alive after idle.
Thread Pool principle:Start some threads in advance, and obtain a task from the task queue for execution in an infinite loop of threads until the thread pool is closed. If a thread terminates due to an exception in executing a task, a new thread is created. This is repeated. 3. thread Pool task submit and execution process. A job is submitted. If the thread pool size does not reach corePoolSize, a worker is started every time, that is, a thread is started to execute B immediately. if the execution is too late, the redundant threads are placed in the workQueue, waiting for the started worker to execute c. if the workQueue of the queue is full, a new worker is started under maximumPoolSize to execute workQueued cyclically. if a task still comes in after maximumPoolSize is started and the thread pool reaches full load, the task will be executed to reject the RejectedExecutionHandler. Java Code
Core code of the thread pool
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
// The process is: when the corePoolSize is not reached, the worker is created for execution and the corePoolSize is reached to join the workQueue. // When the workQueue is full and under maximumPoolSize, create a new worker to reach maximumPoolSize and execute reject Public void execute (Runnable command ){ If (command = null) Throw new NullPointerException ();
// 1: poolSize reaches corePoolSize. Execute 3 to add the task to workQueue. // 2: If poolSize is not reached, execute addIfUnderCorePoolSize () to create a new worker in corePoolSize and execute the task immediately. // If corePoolSize is reached, execute 3 as above If (poolSize> = corePoolSize |! AddIfUnderCorePoolSize (command )){ // 3: When the workQueue is full, execute 5 If (runState = RUNNING & workQueue. offer (command )){ If (runState! = RUNNING | poolSize = 0 ){ // 4: If the thread pool is closed, a denial policy is executed. // If poolSize = 0, a new thread is started to execute tasks in the queue. EnsureQueuedTaskHandled (command ); } // 5: Create a new worker in maximumPoolSize and execute the task immediately. // If maximumPoolSize is reached, execute the 6 denial Policy } Else if (! AddIfUnderMaximumPoolSize (command )) // 6: denial Policy Reject (command); // is shutdown or saturated } } |
The code above shows when a task is submitted.
Run nowWhat about it? RunState = RUNNING & (poolSize <corePoolSize | (workQueue. isFull () & poolSize <maxnumPoolSize) 4. working Worker principle as mentioned above, the thread pool creation thread is actually delegated to the Worker object. Worker cyclically obtains tasks in the work queue to complete Java Code
Worker execution
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Public void run (){ Try { Runnable task = firstTask; FirstTask = null; // GetTask () is a task that is blocked from workQueue,If getTask () returns null, the thread ends. While (task! = Null | (task = getTask ())! = Null ){ RunTask (task ); Task = null; } } Finally { // Here, the worker or thread needs to be closed because the thread pool is closed or exceeds the aliveTime. WorkerDone (this ); } } |
5. Thread destruction keepAliveTime: indicates how long the thread will be destroyed after it is idle, and the thread destruction is implemented through getTask () of worker. Generally, Worker cyclically obtains getTask (),
If getTask () returns null, the worker of the worker end.Then let's see when getTask () returns null. Java Code Worker's getTask Method
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
Runnable getTask (){ For (;;){ Try { Int state = runState; If (state> SHUTDOWN) Return null; Runnable r; If (state = SHUTDOWN) // Help drain queue R = workQueue. poll (); Else if (poolSize> corePoolSize | allowCoreThreadTimeOut) //When poolSize is greater than corePoolSizeOr allow the core thread to time out // If the timeout value is obtained, null may be obtained. The worker thread is destroyed. R = workQueue. poll (keepAliveTime, TimeUnit. NANOSECONDS ); Else R = workQueue. take (); If (r! = Null) Return r; // Check whether the worker thread is destroyed. If (WorkerCanExit ()){ If (runState> = SHUTDOWN) // STOP or TERMINATED to terminate idle worker InterruptIdleWorkers (); Return null;// Here, null is returned, indicating that the worker thread is destroyed. } // Others: retry, continue Loop } Catch (InterruptedException ie ){ // On interruption, re-check runState } } } |
6. close the thread pool gently shut down shutdown: all started tasks are completed, and new tasks are no longer accepted. Close shutdownNow now: cancel all ongoing and unexecuted tasks. For details, refer to source code 7. the thread pool is monitored by the parameters provided by the thread pool. Some attributes in the thread pool can be used to monitor the thread pool.
TaskCount: number of tasks to be executed in the thread pool. CompletedTaskCount: number of tasks completed by the thread pool during running. Less than or equal to taskCount. LargestPoolSize: Maximum number of threads created in the thread pool. This data shows whether the thread pool is full. If it is equal to the maximum size of the thread pool, it indicates that the thread pool has been full. GetPoolSize: Number of threads in the thread pool. If the thread pool is not destroyed, the threads in the pool will not be destroyed automatically, so this size will not increase + getActiveCount: Get the number of active threads.
Monitor by extending the thread pool. By inheriting the thread pool and rewriting the beforeExecute, afterExecute, and terminated methods of the thread pool, we can do something before the task is executed and before the thread pool is closed. For example, the average execution time, maximum execution time, and minimum execution time of monitoring tasks. These methods are empty in the thread pool. 8. Adjust the size of the thread pool by optimizing the thread pool. The optimum size of the thread pool depends on the number of available processors and the nature of tasks in the work queue. Adjusting the thread pool size is basically
Avoid two types of errors: Too few threads or too many threads.A. CPU Limit Tasks to improve CPU utilization. In applications running on N processor machines with computing restrictions, adding additional threads when the number of threads is close to N may improve the total processing capability, when the number of threads exceeds N, adding additional threads does not work. In fact, too many threads may even reduce performance because it will lead to additional Environment switching overhead. If there is only one working queue on a system with N processors, all of which are computing tasks
N or N + 1Generally, the maximum CPU utilization is obtained.
B. I/O-restricted tasks (for example, tasks that read HTTP requests from sockets)The pool size needs to exceed the number of available processors, because not all threads are working all the time. By using the summary analysis, you can obtain some data and calculate the approximate thread pool size. The Amdahl law provides a good approximate formula. WT indicates the average wait time of each task, and ST indicates the average service time (computing time) of each task ). The WT/ST is the percentage of the waiting time of each task. For N-processor systems, the pool can be approximately
N * (1 + WT/ST)Threads.
C. comprehensively consider the performance bottleneck of the thread poolA. processor utilization rate B. as the thread pool grows, you may encounter restrictions on the scheduler, available memory, or other system resources, for example, the number of sockets, opened file handles, or database connections. Reference: http://www.blogjava.net/xylz/archive/2011/01/18/343183.html