• What is a thread pool
  • Why use thread pools
  • Thread pool processing logic
  • How do I use thread pools
  • How to size thread pools properly
  • conclusion

What is a thread pool

A thread pool, as its name implies, is a pool that holds threads. Its purpose is to help us manage threads repeatedly, avoid the overhead of creating a large number of threads, improve response time.

Why thread pools

As a serious siege lion, we don’t want people to look at our code and start making fun of it. New Thread().start() makes our code look cluttered, bloated, and difficult to manage and maintain, so we need to use Thread pools.

Threads are often used in programming to process tasks asynchronously, but there is some overhead associated with the creation and destruction of each thread. If you need to start a new thread each time you execute a task, the creation and destruction of these threads will consume a lot of resources. And threads are “self-contained,” which makes it hard to control, let alone a bunch of threads executing. What thread pools do for us is they save the threads for us to use when we need them, eliminating the need for repeated creation and destruction.

Thread pool processing logic

ThreadPoolExecutor constructor

// A five-argument constructor
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

// The six-argument constructor -1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

// The six-argument constructor -2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

// A seven-argument constructor
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) 
Copy the code

Although many parameters, just look scary, in fact, very easy to understand, the following will be solved.

Let’s take the most parameters:

1. CorePoolSize -> Maximum number of core threads in this thread pool

Core thread: After the thread pool is created, the core thread is not created but is created after the task is received. It will always exist in the thread pool (even if the thread is doing nothing), and when there is a task to be executed, if the core thread is not occupied, the core thread will be used to execute the task first. Generally, set the number to twice the number of CPU cores.

2. MaximumPoolSize -> Maximum number of threads in this thread pool

Total threads = number of core threads + number of non-core threads

Non-core threads: Simple to understand, that is, the core threads are occupied, but there are tasks to do, create a non-core thread

3. KeepAliveTime -> Idle timeout of non-core threads

This parameter can be interpreted as: there are fewer tasks, but there are many threads in the pool. Non-core threads cannot be fed for nothing. After this time, non-core threads will be killed, but core threads will remain.

4.TimeUnit -> keepAliveTime unit

TimeUnit is an enumeration type that includes: MICROSECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 MILLISECONDS: 1ms = 1ms /1000 SECONDS: SECONDS MINUTES: It’s HOURS and DAYS

5.BlockingQueue workQueue -> Task queue in thread pool

By default, the task come in after the first assigned to the core thread execution, core thread if all occupied, do not immediately open non-core threads to execute tasks, but will insert task queue waiting for task execution, core thread from the task queue tasks to perform, task queue can set the maximum, when insert task enough, peak, Non-core threads are created to perform tasks.

There are four common workqueues:

SynchronousQueue: When a task is received on the SynchronousQueue, it is submitted directly to the thread instead of being retained. What if all threads are working? Create a new thread to handle the task! Therefore, maximumPoolSize is usually specified as integer. MAX_VALUE, or infinite, when using this type of queue to ensure that no new thread can be created when the number of threads reaches maximumPoolSize

2.LinkedBlockingQueue: When this queue receives a task, if the number of core threads that have been created is less than the maximum number of core threads in the thread pool, a new thread (core thread) will process the task. If the number of created core threads is equal to the maximum number of core threads, the queue is queued. Since there is no maximum limit on this queue, any task that exceeds the number of core threads will be added to the queue, which invalidates maximumPoolSize because the total number of threads will never exceed corePoolSize

3. The ArrayBlockingQueue: If the queue is full, a new thread (non-core thread) will execute the task. If the queue is full, a new thread (non-core thread) will execute the task. If the total number of threads reaches maximumPoolSize, a new thread will execute the task. And the queue is full, an error occurs, or an implementation-defined saturation policy is executed

4.DelayQueue: The elements in the queue must implement the Delayed interface, which means that the task you sent in must implement the Delayed interface first. When the queue receives a task, it joins the queue first. The task will be executed only after the specified delay time is reached

6.ThreadFactory ThreadFactory -> Factory for creating threads

You can use a thread factory to give each created thread a name. Generally, you do not need to set this parameter.

7. RejectedExecutionHandler handler – > saturated strategy

This is when the task queue and fill the thread pool, the coping strategies taken by default is AbordPolicy, unable to process a new task, and throw RejectedExecutionException anomalies. There are three other strategies, as follows. (1) CallerRunsPolicy: use the caller’s thread to process the task. This strategy provides a simple feedback control mechanism that slows down the delivery of new tasks. (2) DiscardPolicy: indicates that the task cannot be executed and is deleted. DiscardOldestPolicy: Discards the latest task in the queue and executes the current task.

Don’t be dizzy, the next picture, I believe you can be combined with the picture

How do I use thread pools

Java provides four types of thread pools: FixedThreadPool, CachedThreadPool, SingleThreadExecutor, and ScheduledThreadPool.

1.FixedThreadPool

* If the threads exceed the specified number of threads, wait in the queue. * If the threads exceed the specified number of threads, wait in the queue.

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
Copy the code

FixedThreadPool corePoolSize and maximumPoolSize are both set to nThreads, that is, there are only a fixed number of core threads and no non-core threads. KeepAliveTime of 0L means that redundant threads are terminated immediately. Since no redundant threads are created, this parameter is invalid. The FixedThreadPool task queue uses LinkedBlockingQueue.

public static void main(String[] args) {
        // The argument is the maximum number of threads to pool
        ExecutorService executorService = Executors.newFixedThreadPool(10);
}
Copy the code

2.CachedThreadPool

CachedThreadPool is a thread pool that creates threads as needed

public static ExecutorService newCachedThreadPool(a) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
Copy the code

CorePoolSize of CachedThreadPool is 0, maximumPoolSize is the maximum value of Int. KeepAliveTime is 60 seconds, which means that an idle thread waits 60 seconds for a new task. The queue used here is the blocking queue SynchronousQueue, which has no buffer, so it can only have one element at most, and blocks waiting for new tasks.

3.SingleThreadExecutor

SingleThreadExecutor is a thread pool that works with a single thread. Its creation source code is as follows:

    public static ExecutorService newSingleThreadExecutor(a) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1.1.0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
Copy the code

We can see that the total number of threads and the number of core threads are both 1, so there is only one core thread. It is the thread pool that blocks the LinkedBlockingQueue with a list, first in, first out, so that tasks proceed sequentially.

4.ScheduledThreadPool

ScheduledThreadPool is a thread pool that can implement scheduled and periodic tasks.

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
Copy the code

Here created ScheduledThreadPoolExecutor, inherited from ThreadPoolExecutor, mainly used for timing delay or processing tasks on a regular basis. ScheduledThreadPoolExecutor structure is as follows:

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }
Copy the code

CorePoolSize is a fixed value that was passed in. MaximumPoolSize is infinitely large. Since the DelayedWorkQueue is unsolver, the maximumPoolSize parameter is invalid. The thread pool executes as follows:

scheduleAtFixedRate
scheduleWithFixedDelay
DelayedWorkQueue
RunnableScheduledFuture
ScheduledFutureTask
corePoolSize
ScheduledFutureTask
corePoolSize
DelayedWorkQueue
DelayedWorkQueue
ScheduledFutureTask
time
DelayedWorkQueue

How to size thread pools properly

Generally, you need to configure the thread pool size according to the type of task: If the task is CPU intensive, you need to squeeze as much CPU as possible, and the reference value can be set to NCPU+1. If the task is IO intensive, you can set the reference value to 2*NCPU. Of course, this is only a reference value. Then observe the task running status, system load, and resource utilization to adjust.

conclusion

Java for us to provide a thread pool on the introduction to this, wall crack suggested we still start to knock a knock, after all, the practice of the heart just know.