Compared with the thread pool, we may have more contact with the new Thread. Since we have the new Thread, why should we use the thread pool?
Disadvantages of new Thread
a. Every time a new Thread creates a new object, the performance is poor
b. There is a lack of unified management of threads, and there may be unlimited new threads to compete with each other, which may occupy too much system resources and cause a crash or OOM (OutOfMemory)
c, lack of more features, such as more execution, regular execution, thread interruption
Advantages of thread pools
a. Reuse existing threads, reduce the overhead of object creation and death, and have good performance
b. It can effectively control the maximum number of concurrent threads, improve system resource utilization, and avoid excessive resource competition and blocking
c. Provide functions such as timed execution, regular execution, single thread, and concurrent number control
thread pool class diagram
As can be seen from the class diagram, the ThreadPoolExecutor class is the core class in the thread pool. Let's focus on analysis
ThreadPoolExecutor construction parameters
a. corePoolSize: the number of core threads. By default (threads can be pre-created), the number of threads in the thread pool after the thread pool is 0. When there is a task, a thread will be created to execute the task. After the number of threads reaches corePoolSize, the arriving tasks will be put into the cache queue
b, maximumPoolSize: the maximum number of threads
c. workQueue: Blocking queue, storing tasks waiting to be executed, there are three values, ArrayBlockQueue (array-based first-in, first-out queue, the size must be specified when creating), LinkedBlockingQueue (first-in, first-out queue based on linked list, if this queue is not specified Size, the default is Integer.MAX_VALUE), SynchronousQueue (the submitted task will not be saved, and a new thread will be created to execute the new task)
d. keepAliveTime: how long the thread can be terminated when there is no task execution. By default, keepAliveTIme will only work when the number of threads in the thread pool is greater than corePoolSize. When the number of threads in the thread pool is greater than corePoolSize, if a thread is idle When the time reaches keepAliveTime, it will terminate. If the allowCoreThreadTimeOut(boolean) method is called, the keepAliveTime parameter will also work when the number of threads in the thread pool is not greater than corePoolSize, until the number of threads in the thread pool is 0
e. unit: the time unit of keepAliveTime, there are 7 values, such as: TimeUnit.DAYS; days, which can be specific to nanoseconds
f, threadFactory: thread factory, used to create threads
g. rejectHandler: When refusing to process tasks, there are usually four values, ThreadPoolExecutor.AbortPolicy: discard tasks and throw RejectedExecutionException; ThreadPoolExecutor.DiscardPolicy: discard tasks, but do not throw exceptions; ThreadPoolExecutor.DiscardOldestPolicy: discard queues Foremost task, retry the task (repeat this process); ThreadPoolExecutor.CallerRunsPolicy: The task is handled by the calling thread
The relationship between the parameters is as follows:
a. If the current poolsize is less than corePoolSize, create a new thread to execute the task
b. If the current poolsize is greater than corePoolsize and the waiting queue is not full, enter the waiting queue
c. If the current poolsize is greater than corePoolsize and less than maximumPoolSize, and the waiting queue is full, create a new thread to execute the task
d. If the current poolsize is greater than corePoolSize and greater than maximumPoolSize, and the waiting queue is full, the rejection policy is used to process the task
e. The thread in the thread pool will not exit immediately after executing the task, but will check whether there is a new thread to execute in the waiting queue. If the new task cannot be waited for in keepAliveTime, the thread will exit
ThreadPoolExecutor state
What should be noted in the figure is the difference between the shutdown() and shutdownNow() methods. After executing the former, the thread that is still executing will be executed and then closed. After executing the latter, the thread pool will be closed immediately, and the executing thread will no longer be executed.
ThreadPoolExecutor方法
a, execute(): submit the task and hand it over to the thread pool for execution
b, submit(): submit the task, can return the execution result execute+Future
c. shutdown(): close the thread pool and wait for all tasks to be executed
d. shutdownNow(): closes the thread pool without waiting for the task to finish executing
e. getTaskCount(): The total number of tasks executed and not executed by the thread pool
f. getCompletedTaskCount(): the number of completed tasks
g. getPoolSize(): the current number of threads in the thread pool
h, getActiveCount(): the number of threads currently executing tasks in the thread pool
Executors class
As can be seen from the above class diagram, Executors class provides some tool methods for Executor, ExecutorService, ScheduledExecutorService, and Executors encapsulates ScheduledThreadPoolExecutor class and ThreadPoolExecutor class, so we advocate using this class in actual use, Executors class provides four static method:
a. newCachedThreadPool: Create a cacheable thread pool to flexibly recycle idle threads. If there are no recyclable threads, create new threads
As can be seen from the above figure, newCachedThreadPool sets corePoolSize to 0, sets maximumPoolSize to Integer.MAX_VALUE, and uses SynchronousQueue, that is to say, if there is a new task, the new thread will run, and if the thread is idle for more than 60 seconds, the thread will be destroyed. The demo code is as follows:
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(new Runnable() { @Override public void run() { log.info("task:{}", index); } }); } executorService.shutdown(); }
b. newFixedThreadPool: Create a fixed-length thread pool, which can control the maximum number of concurrent threads, and the excess threads will wait in the queue
The value of corePoolSize and maximumPoolSize of the thread pool created by newFixedThreadPool are equal, and the thread is destroyed directly after being idle, using LinkedBlockingQueue. The demo code is as follows:
public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(new Runnable() { @Override public void run() { log.info("task:{}", index); } }); } executorService.shutdown(); }
c. newScheduledThreadPool: Create a thread pool with unlimited size (maximum Integer.MAX_VALUE), support timing and periodic task execution
The feature of newScheduledThreadPool is that it can perform task scheduling. The most commonly used methods are ScheduleAtFixedRate (task scheduling based on fixed time intervals) and ScheduleWithFixedDelay (task scheduling based on irregular time intervals, mainly depending on the length of the task execution time). The demo code is as follows:
public static void main(String[] args) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); executorService.schedule(new Runnable() { @Override public void run() { log.warn("schedule run"); } },3,TimeUnit.SECONDS); executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { log.warn("schedule run"); } },1,3,TimeUnit.SECONDS); }
d. newSingleThreadExecutor: Create a single-threaded thread pool, and only use a unique thread to execute tasks
newSingleThreadExecutor fixes both corePoolSize and maximumPoolSize to 1, and directly destroys the thread when it is idle. The LinkedBlockingQueue.demo code used is as follows:
public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(new Runnable() { @Override public void run() { log.info("task:{}", index); } }); } executorService.shutdown(); }
Reasonable configuration of thread pool
The specific cooperation of the thread pool needs to be adjusted according to the actual situation. The following are two reference principles. You can set this principle first and then adjust it according to the system load and resource utilization.
a. For CPU-intensive tasks, it is necessary to squeeze the CPU as much as possible. The reference value can be set to ncpu+1;
b. For io-intensive tasks, the reference value can be set to 2*ncpu