1. Why use thread pools?
For example, we currently have a task that outputs the name of the current thread. Then we create a task, create a thread, give the task to the thread, and then start the thread.
public class Task implements Runnable{ @Override public void run() { //Outputs the name of the current thread System.out.println(Thread.currentThread().getName()); } }
public class ThreadPool001 { public static void main(String[] args) { //Create task Runnable task = new Task(); //Create thread Thread thread = new Thread(task); //Start thread thread.start(); } }
Output results: "C:\Program Files\Java\jdk1.8.0\bin\java.exe" Thread-0
Our thread can only execute one task. We can't create multiple tasks and put them on the same thread for continuous execution. We can only re create other threads and start and execute them. The thread will be destroyed after executing the task. If there are more tasks to execute, you need to recreate the thread. The problem is that * * threads cannot be reused. Creating and destroying threads repeatedly will take time and resources. * * at this time, we should consider if threads can be reused. The advantage is to save time and resources. We can see how the thread pool performs tasks.
public class ThreadPool001 { public static void main(String[] args) { //Create task Runnable task1 = new Task(); Runnable task2 = new Task(); Runnable task3 = new Task(); //Create a thread pool with only one thread ExecutorService threadPool = Executors.newSingleThreadExecutor(); //Submit task threadPool.execute(task1); threadPool.execute(task2); threadPool.execute(task3); //The thread pool will automatically allocate threads to execute the submitted tasks. Finally, you need to call the shutdown method to close the thread pool threadPool.shutdown(); } }
We create a threadPool with only one thread and submit the created three tasks to the thread pool. The thread pool will automatically allocate threads to execute the submitted tasks. Finally, we need to call the shutdown method to close the thread pool. When the thread pool is closed, no more tasks can be submitted.
Output results: "C:\Program Files\Java\jdk1.8.0\bin\java.exe" pool-1-thread-1 pool-1-thread-1 pool-1-thread-1
From the results, we can see that a thread performs three tasks. At this time, the front thread can be reused. Compared with threads, thread pools have many advantages. Here are three:
- Reduce resource consumption: reduce the consumption caused by thread creation and destruction by reusing the created threads;
- Improve response speed: when there is a task, it can be executed immediately without waiting for the thread to be created;
- Improve thread manageability: thread pools can be uniformly allocated, tuned and monitored.
Threads are scarce resources in our system. Unlimited and repeated creation not only consumes system resources, but also reduces the stability of the system.
2. What is a thread pool?
Thread Pool is a tool to manage threads based on the idea of pooling. The Thread Pool creates threads in advance. When a task needs to be executed, it submits the task to the Thread Pool. The Thread Pool allocates threads to execute. The threads in the Thread Pool can also be reused. After executing a thread, it can then execute other tasks. When all tasks are executed, we can choose to close the Thread Pool or wait for receiving tasks.
UML class diagram of ThreadPool:
3. Why is it recommended to create thread pools natively?
There are eight ways to create a thread pool, but they never change. They are the way to create a thread pool natively. This method is also highly recommended by Ali. The manual explains that thread pools are not allowed to be created by Executors, but by ThreadPoolExecutor. Other methods are at risk of resource consumption.
From the above class diagram, we can see that only ThreadPoolExecutor and ScheduleThreadPoolExecutor can be instantiated. ThreadPoolExecutor is the core class of thread pool. The core class has four construction methods, but they are similar. We can learn from the last construction method with the most parameters.
The first type: most parameters
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
Parameter meaning:
corePoolSize: number of core threads in thread pool int type
maximumPoolSize: the maximum number of threads allowed in the thread pool. Type int
keepAliveTime: the idle time allowed by threads in the thread pool. long type
Unit: the unit of idle time allowed by thread pool maintenance threads
MICROSECONDS Microsecond one millionth of a second/1000) MILLISECONDS Millisecond thousandth of a second NANOSECONDS Nanosecond one billionth of a second/1000) SECONDS second MINUTES minute HOURS hour DAYS day
workQueue: the buffer queue used by the thread pool
handler: the processing policy of the thread pool for rejecting tasks
ThreadPoolExecutor.DiscardPolicy() discards the current task.
threadFactory: thread factory, which is mainly used to create threads: the default value is DefaultThreadFactory
For example:
corePoolSize: 10
maximumPoolSize: 25
keepAliveTime: 10
unit: TimeUnit.SECONDS
The idle thread has a survival time of 10 seconds and will be destroyed if there is no work within 10 seconds. If the idle time is 0 seconds, the idle thread will not be destroyed. There are 10 core threads in the thread pool. If the thread pool is not closed, the 10 core threads will not be destroyed. The maximum number of threads in the thread pool is 25. Threads other than core threads are non core threads, and non core threads are not allowed The execution task will be cleaned up and destroyed. How long it can survive before being cleaned up and destroyed depends on keepAliveTime and unit.
workQueue task queue: the tasks of the thread pool are stored in this container, and the threads in the thread pool also obtain tasks from this container. The commonly used task queues are linked blocklingqueue chain blocking queue based on linked list and ArrayBlocklingQueue array blocking queue based on array.
threadFactory thread factory: This is an interface. You can customize the thread related settings by implementing its internal newThread method. For example, you can specify the thread name to view the thread execution in the later log; you can also specify whether the thread can be a background thread, etc.
handler thread rejection policy: if the following four conditions are met at the same time, the tasks submitted to the thread pool will be rejected.
- Threads in the thread pool are full
- Unable to continue capacity expansion
- There are no idle threads, and all threads are executing tasks
- The task queue is full and new tasks cannot be saved
Code example:
Task class: outputs the name of the current thread public class Task implements Runnable{ @Override public void run() { //Outputs the name of the current thread System.out.println(Thread.currentThread().getName()); } }
Thread factory class: public class CustomThreadFactory implements ThreadFactory { //Define a counter and number the thread. The initial number is 1 to avoid thread safety problems private final AtomicInteger i = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { //Create a thread and specify a task Thread thread = new Thread(r); //Set thread name thread.setName("Thread:"+i.getAndIncrement()+"number"); //Return thread return thread; } }
public class ThreadPool001 { public static void main(String[] args) { //Create task Runnable task1 = new Task(); Runnable task2 = new Task(); Runnable task3 = new Task(); //Create thread pool ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 25, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new CustomThreadFactory(), //Thread factory just created new AbortPolicy());//Default reject policy //Submit task threadPool.execute(task1); threadPool.execute(task2); threadPool.execute(task3); //Close thread pool threadPool.shutdown(); } }
Output results: "C:\Program Files\Java\jdk1.8.0\bin\java.exe" Thread: 1 Thread: 3 Thread: 2