What is a thread pool?

A thread pool can be thought of as a collection of threads with multiple threads.



2. Benefits of using thread pools

  • Reduce resource consumption. Reduce the cost of thread creation and destruction by reusing already created threads.
  • Improve response time. When a task arrives, it can be executed immediately without waiting for the thread to be created.
  • Improve thread manageability. Threads are scarce resources. If created without limit, they will not only consume system resources, but also reduce the stability of the system. Thread pools can be used for unified allocation, tuning and monitoring.



3. The core parameters of the thread pool


CorePoolSize number of core threads. If the number of core threads is not reached, a new thread will be created. When the core number of threads is reached, the tasks are queued

MaximumPoolSize maximum number of threads, which can be 2.1 billion Integer.MAX_VALUE. When the core number of threads is reached and the queue is full, additional threads are created to perform the task, up to the maximum number of threads

KeepAliveTime Lifetime, when the task is completed and the additional thread lives for a certain period of time, it will self-destruct. Ile wait time (This parameter is not valid for the core thread by default. When AllowCoreThreadTimeout is manually set to true, the core thread will not be destroyed until it has lived longer.)

TimeUnit A unit of idle wait time

BlockingQueue: The task comes in. If the core thread count is full, the task enters the queue and waits.

The ThreadFactory thread creates the factory

RejectExecutionHandler rejections policy, which can be enabled if additional tasks enter when the maximum number of threads is full and the queue is full.


Reference source

/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters and default thread factory and rejected execution handler. * It may be more convenient to use one of the {@link Executors} factory * methods instead of  this general purpose constructor. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param 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. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }



4. Processing flow of thread pool



What are the ways to create a thread pool?


Creates the specified thread pool from the Executors utility class

throughnew ThreadPoolExecutor()Custom thread pool, passing in the specified parameters



6. Common thread pools and their usage scenarios


NewFixedThreadPool () : A pool with a fixed number of threads

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

Thread pool features:

  • The number of core threads is the same as the maximum number of threads
  • There is no such thing as non-idle time where keepAliveTime is 0
  • The blocking queue is an unbounded LinkedBlockingQueue

disadvantages

  • If a task takes too long to execute and causes a large number of tasks to pile up in the blocking queue, or a large number of tasks to come in at any given moment, the machine memory usage will skyrocket and eventually lead to OOM

Usage scenarios

NewFixedThreadPool is designed to handle CPU-intensive tasks, ensuring that the CPU is used by worker threads for a long period of time and that as few threads as possible are allocated, that is, suitable for executing long-term tasks.


newCachedThreadPool()

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

Thread pool features:

  • The number of core threads is 0
  • The maximum number of threads is INTEGER.MAX_VALUE
  • A blocking queue is a synchronousQueue
  • The idle life of a non-core thread is 60 seconds

disadvantages

  • If the task is submitted faster than the thread can process the task, then new threads will be created constantly and in extreme cases, CPU and memory resources will be exhausted
  • CachedThreadPoolThe number of threads allowed to be created is INTEGER.MAX_VALUE, which may create a large number of threads, resulting in OOM.

A task queue uses a synchronousQueue, which cannot be inserted into a task and is executed as soon as it is available

Usage scenarios

Suitable for executing a large number of short – term small tasks concurrently.


newSingleThreadExecutor()

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

Thread pool features

  • Number of core threads is 1
  • The maximum number of threads is also 1
  • The blocking queue is the LinkedBlockingQueue
  • KeepAliveTime of 0

disadvantages

  • LinkedBlockingQueue is an unbounded queue that may cause OOM

Usage scenarios

  • Suitable for scenarios where tasks are executed serially, task by task.

newScheduledThreadPool()

public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
          new DelayedWorkQueue());
}

Thread pool features

  • The maximum number of threads is INTEGER.MAX_VALUE
  • The blocking queue is a DelayedWorkQueue
  • KeepAliveTime of 0
  • ScheduleAtFixedRate () : Executes at a certain rate period
  • ScheduleWithFixedDelay () : Executes after a delay

Usage scenarios

Scenarios where tasks are executed periodically and where the number of threads needs to be limited



Are there any threads in the thread pool after it is created?


There will be no threads if no tasks come in after the thread pool is created.



8. Do you know any method to preheat the thread pool?


== Thread preheating can use the following two methods ==

1. Start only one thread to warm up

2. All start and preheat



9. What is the state of the thread pool?

Reference source

Running private final atomicInteger CTL = new atomicInteger (ctlOf(Running, 0)); Private static final int RUNNING = -1 << COUNT_BITS; private static final int RUNNING = -1 << COUNT_BITS; private static final int SHUTDOWN = 0 << COUNT_BITS; private static final int STOP = 1 << COUNT_BITS; private static final int TIDYING = 2 << COUNT_BITS; private static final int TERMINATED = 3 << COUNT_BITS;

1, RUNNING

  1. When the thread pool is in RUNNING state, it can receive new tasks and process the added tasks.
  2. State switch: The initial state of the thread pool is RUNNING. In other words, once the thread pool is created, it is RUNNING, and the number of tasks in the thread pool is zero!

2, ShutDown

  1. When the thread pool is SHUTDOWN, it does not receive new tasks, but it can process added tasks.
  2. State switch: When a thread pool is called shutdown(), the pool is shutdown by run->.

3, STOP

  1. When the thread pool is in the STOP state, no new tasks will be received, no added tasks will be processed, and the task being processed will be interrupted.
  2. State switch: When shutdownNow() of the thread pool is called, the thread pool is stopped by (RUNNING or SHUTDOWN) ->.

4, tidying

  1. When all tasks have been terminated and the “number of tasks” recorded by CTL is 0, the thread pool will become TIDYING state. Hook function terminated() when the thread pool is in the Tidying state. Terminated () is empty in the ThreadPoolExecutor class, and the user will process it if the thread pool becomes TIDYING. This can be done by overloading the terminated() function.
  2. State switch: When the thread pool is in the SHUTDOWN state, the blocking queue is empty and the tasks executed in the thread pool are also empty, then the SHUTDOWN -> TIDYING occurs. When the thread pool is in the STOP state and the task executing in the thread pool is empty, it is stopped -> TIDYING.

5. TERMINATED (TERMINATED)

  1. State: The thread pool terminates completely and becomes TERMINATED.
  2. State toggling: Tidying -> terminated after execution of the thread pool in the TIDYING state.



What are the rejection policies for the thread pool?

AbortPolicy (default), directly to throw out a type of RejectedExecutionException RuntimeException exceptions to prevent normal operation of the system.

DiscardPolicy: The task is simply discarded without any processing or exception thrown. This is the best option if tasks are allowed to be lost.

Discard the longest-waiting task in your queue and then put the current task on the queue to attempt to submit the task again.

CelerRunspolicy: “Caller runs” a mediation mechanism that neither abandons tasks nor throws exceptions, but instead pushes some tasks back to the caller, reducing the traffic for new tasks.



11. How to configure the number of threads in the thread pool?

Determine whether the current task is CPU intensive or IO intensive

The formula

  • CPU-intensive tasks (N+1) : This type of task consumes CPU resources primarily. You can set the number of threads to N (CPU cores) +1. The extra thread is used to prevent occasional page failures or other causes of task pauses. Once the task is paused, the CPU is idle, and the extra thread in this case can take advantage of the idle time of the CPU.
  • I/O intensive tasks (2N) : In this case, the system will spend most of the time processing I/O interactions, and the thread will not use the CPU during the I/O processing time, so the CPU can be surrendered to another thread. Therefore, in the application of I/O intensive tasks, we can configure more threads, the specific calculation method is 2N.



12, The difference between execute and submit

1. Different sources of methods

Execut () is defined in the top-level interface Executor of the thread pool, and there is only one interface, which shows the importance of this method.

public interface Executor {
    void execute(Runnable command);
}

A concrete implementation of this is found in the ThreadPoolExecutor class.

Submit () is defined in the ExecutorService interface and defines three types of overloading, which you can see in the JDK documentation

<T> Future<T> submit(Callable<T> task); Future<? > submit(Runnable task); <T> Future<T> submit(Runnable task, T result);

2. Different acceptance parameters

The execute() method can only accept tasks that implement the Runnable interface type and the submit() method can accept tasks of either a Runnable or a Callable type.


3. The return value is different

The return value of execute() is void. The return value of the thread cannot be obtained after the thread commits. The return value of submit() is Future, and the return value of the thread execution is obtained from the Future’s get() method. The get() method is synchronized, and if the thread has not finished executing, it will wait until the thread finishes executing.

although
submit()Method can be submitted
RunnableType, but when the get() of the Future method is executed, the thread returns it
null, there will be no actual return value, because Runable has no return value to begin with


4. Different exception handling

Execute will throw an exception if it encounters an exception while executing the task.

Submit, on the other hand, does not throw an exception directly; it only throws an exception when you call the Future’s get method to get the return value.



Stern said

I am aCode pipi shrimpIn the future, we will continue to update the useful blog posts for you, and look forward to your attention!!

It is not easy to create, if this blog post is helpful to you, I hope you can == thumb up and pay attention to me, thank you for your support, we will see you next time ~~~

== Share outline ==

Big factory interview topic column






Java from entry to the grave learning route directory index






Open source crawler example tutorial directory index

More exciting content to share, please clickHello World (low low ◡)