The Background of Thread Pool Technology
In the application server side, a large number of tasks from users need to be processed. If a new thread is adopted to process each task received by the server side, and then it is closed when the thread ends, it will cause a large number of context switching, and the resource occupied by the creation and destruction of threads is also huge, which results in a waste of resources. By using thread pool technology, we can create a number of threads in advance, and the management of these threads is not handed to users, so we can repeatedly use the created threads to perform tasks, thus avoiding the resource consumption of thread creation and destruction, and reducing the context switching, because the number of threads is reduced.
Benefits of thread pooling
- Reducing resource depletion
- Increase response speed
- Improving thread manageability
Implementing Principle of Thread Pool
Processing flow of thread pool:
(1) The thread pool determines whether all threads in the core thread pool are working, and if not, creates a new thread to perform tasks.
(2) Thread pool judges whether the work queue is full or not, and puts the task into the work queue when it is not.
(3) The thread pool determines whether all threads in the thread pool are working, and if not, creates a new thread to perform tasks.
(4) Failure to perform tasks
Method Source Analysis for Thread Pool Task Execution (source code in JDK8):
public void execute(Runnable command) {
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//If the current number of threads is less than the number of core threads, create threads and execute tasks immediately
if (workerCountOf(c) < corePoolSize) {
//Automatically verify the working status and number of threads
if (addWorker(command, true))
return;
//Get the number of threads in working state
c = ctl.get();
}
//Double-checking whether tasks should be added to the work queue because of the death of other threads after the first check and the closure of the thread pool
//Ensure that no threads die
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//The new task has been run and the task is removed from the task queue
if (! isRunning(recheck) && remove(command))
reject(command);
//Thread pool closed
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//If the maximum number of threads is not reached, a new thread is added for task processing. Failure indicates that the thread pool refuses to perform overloaded tasks.
else if (!addWorker(command, false))
reject(command);
}
Workthreads encapsulate threads into worker threads Work, and in the run method, they constantly get tasks from the work queue to execute them.
Reasonable configuration of thread pool
For CPU-intensive servers, we should configure as few threads as possible, such as N (number of CPUs) +1 thread pool. For IO-intensive tasks, we should configure as many threads as possible, such as 2 x N (number of CPUs) thread pool. Because IO is dense, it takes longer to process IO operations. If the number of threads is too small, the CPU will be idle at this time, and there are many tasks waiting to be executed, so the execution efficiency is very low.
Thread pool monitoring
The throughput of the thread pool and the best configuration parameters can be obtained by monitoring the number of tasks that the thread pool needs to perform (taskCount), the number of tasks to perform (Completed TaskCount), and the largest thread creation (largest Pool Size).