Table of contents
ThreadPoolExecutor core parameters
Why is it not recommended to use Executors to create thread pools
Why is there a thread pool?
In actual use, the time spent by the server on creating and destroying threads and the consumption of system resources are quite large, so the number of times to create and destroy threads should be reduced as much as possible.
Since there is no consumption of thread creation and destruction, the system response speed can be improved and
threads can be managed reasonably
thread pool status
1、RUNNING
Status Description: When the thread pool is in the RUNNING state, it can receive new tasks and process added tasks.
2. SHUTDOWNStatus Description: When the thread pool is in the SHUTDOWN state, it does not receive new tasks, but can process added tasks
3, STOPStatus Description: When the thread pool is in the STOP state, it does not receive new tasks, does not process added tasks, and interrupts the tasks being processed.
4. TIDYINGStatus description: When all tasks are terminated and the number of tasks recorded by ctl is 0, the state of the thread pool will change to TIDYING state; when the state of the thread pool changes to TIDYING state, the hook function terminated() will be called. The ThreadPoolExecutor is empty. If the user wants to perform corresponding processing when the thread pool becomes TIDYING, the terminated() function needs to be overloaded.
When the thread pool is STOP, when the tasks executed in the thread pool are empty, it will STOP->TIDYING
5, TERMINATEDStatus Description: When the thread pool is completely terminated, it will become TERMINATED
ThreadPoolExecutor core parameters
corePoolSize
corePoolSize – the number of threads to keep in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
The number of threads held in the pool, even if they are idle, unless allowCoreThreadTimeOut is set
The number of core threads in the thread pool. When a task is submitted, the thread pool creates a new thread to execute the task until the current number of threads is equal to corePoolSize. Even if there are other idle threads that can execute new tasks, threads will continue to be created;
If the prestartAllCoreThreads() method of the thread pool is executed, the thread pool will create and start all core threads in advance.
the case
The number of core threads and the maximum number of threads are 1, using a blocking queue that does not store elements.
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 2; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
Thread.sleep(1000);
}
}
output
pool-1-thread-1
pool-1-thread-1
maximumPoolSize
maximumPoolSize – the maximum number of threads to allow in the pool
The maximum number of threads allowed to exist in the pool
the case
The number of core threads is 1, the maximum number of threads is 3, and a blocking queue that does not store elements is used . (Pay attention to eating in combination with workQueue parameters~ )
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 3; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
output
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
keepAliveTime
keepAliveTime – when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating.
When the number of threads is greater than cores, this is the maximum time that excess idle threads will wait for new tasks before terminating.
The survival time when the thread is idle, that is, when the thread has no tasks to execute, the thread continues to survive; by default, this parameter is only useful when the number of threads is greater than corePoolSize, and idle threads exceeding this time will be terminated;
unit
unit – the time unit for the keepAliveTime argument
Units of the keepAliveTime parameter
workQueue
workQueue – the queue to use for holding tasks before they are executed. This queue will hold only the Runnable tasks submitted by the execute method.
Used to place unexecuted tasks, this queue will only save runnable tasks submitted by the execute method.
A blocking queue used to hold tasks waiting to be executed.
If the current number of threads is corePoolSize, the tasks that continue to be submitted are saved in the blocking queue and wait to be executed;
If the current blocking queue is full and the task continues to be submitted, a new thread will be created to execute the task, provided that the current number of threads is less than the maximumPoolSize; when the blocking queue is an unbounded queue, the maximumPoolSize will not work because it cannot be submitted to the core thread pool Threads will be continuously put into workQueue.
The JDK provides the following queues:
ArrayBlockingQueue
: A bounded blocking queue based on an array structure, sorting tasks by FIFO; (commonly used)
LinkedBlockingQueue
: Blocking queue based on linked list structure, sorting tasks by FIFO, the throughput is usually higher than ArrayBlockingQueue; (commonly used)
SynchronousQueue
: A blocking queue that does not store elements, each insertion operation must wait until another thread calls the removal operation, otherwise the insertion operation is always blocked, and the throughput is usually higher than LinkedBlockingQueue; (commonly used)
PriorityBlockingQueue
: Unbounded blocking queue with priority;DelayQueue: An unbounded blocking queue implemented using a priority queue.
LinkedTransferQueue: An unbounded blocking queue composed of a linked list structure.
LinkedBlockingDeque: A bidirectional blocking queue composed of a linked list structure.
the case
The number of core threads is 1, the maximum number of threads is 3, and a queue with a capacity of 1 is used .
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 3, 0,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 3; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
output
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
threadFactory
threadFactory – the factory to use when the executor creates a new thread
Create a factory for executors to create threads
Through the custom thread factory, a recognizable thread name can be set for each newly created thread. Defaults to DefaultThreadFactory.
the case
Give the thread a name. I use classes in spring. If you don't want to introduce too many dependencies, you can write a class to change the namePrefix by imitating the code of Executors.defaultThreadFactory().
public static void main(String[] args){
CustomizableThreadFactory customizableThreadFactory = new CustomizableThreadFactory("mine-");//import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
customizableThreadFactory,
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 1; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
output
mine-1
handler
handler – the handler to use when execution is blocked because the thread bounds and queue capacities are reached
Handler to use when thread boundaries and queue capacity are reached to block execution
The saturation strategy of the thread pool. When the blocking queue is full and there are no idle worker threads, if you continue to submit tasks, you must adopt a strategy to process the task. The thread pool provides 4 strategies:
AbortPolicy
: Throw an exception directly, the default strategy;
CallerRunsPolicy
: Use the thread where the caller is located to execute the task;
DiscardOldestPolicy
: Discard the top task in the blocking queue and execute the current task;
DiscardPolicy
: discard the task directly;
the case
by CallerRunsPolicy为案例。核心和最大线程数为1。
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1, 0,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0; i < 3; i++) {
executor.execute(()->{
System.out.println(Thread.currentThread().getName());
});
}
}
output
main
main
pool-1-thread-1
How to close the thread pool
shutdown:
- Modify the thread pool status to
SHUTDOWN
- No longer accepting new submissions
- Interrupt idle threads in the thread pool
- Step 3 only interrupts the idle thread, but the tasks being executed and the tasks in the thread pool task queue will continue to be executed
shutdownNow:
- Modify the thread pool status to
STOP
- No longer accepting task submissions
- Attempt to interrupt all threads in the thread pool (including executing threads)
- Returns a list of tasks that are waiting to be executed
List<Runnable>
Why is it not recommended to use Executors to create thread pools
- newFixedThreadPool and newSingleThreadExecutor: The blocking queue is an unbounded queue. The main problem is that the accumulated request processing queue may consume a very large amount of memory, or even OOM.
- newCachedThreadPool and newScheduledThreadPool: The maximum number of threads is Integer.MAX_VALUE, which may create a very large number of threads, or even OOM.
To be continued... If you think about it, analyze the source code