Comprehensive analysis of java thread pool

Time:2021-1-22

Java – five thread pools, four denial policies, three blocking queues

  • There are three kinds of blocking queues

    BlockingQueue<Runnable> workQueue = null;
Workqueue = new arrayblockingqueue < > (5); / / array based FIFO queue, bounded
Workqueue = new linkedblockingqueue < > (); / / linked list based FIFO queue, unbounded
Workqueue = new synchronous queue < > (); / / unbounded waiting queue

  • There are four rejection strategies

    RejectedExecutionHandler rejected = null;
    rejected = new ThreadPoolExecutor.AbortPolicy (); / / by default, if the queue is full, an exception will be thrown when the task is lost
    rejected = new ThreadPoolExecutor.DiscardPolicy (); / / the queue is full and the task is not lost
    rejected = new ThreadPoolExecutor.DiscardOldestPolicy (); / / delete the task that first entered the queue, and then try to join the queue
    rejected = new ThreadPoolExecutor.CallerRunsPolicy (); / / if adding to the thread pool fails, the main thread will execute the task itself

  • Five thread pools:

    ExecutorService threadPool = null;
    threadPool = Executors.newCachedThreadPool (); / / buffered thread pool, number of threads controlled by JVM
    threadPool = Executors.newFixedThreadPool (3) ; / / fixed size thread pool
    threadPool = Executors.newScheduledThreadPool(2);
    threadPool = Executors.newSingleThreadExecutor (); / / single thread pool, only one thread is working
ThreadPool = new threadpoolexecutor(); / / the default thread pool, with many controllable parameters

1. What is thread pool

It’s very simple. Just look at the name and you can see that it’s a pool with threads. We can hand over the multithreads to be executed to the thread pool for processing, just like the concept of connection pool,By maintaining a certain number of thread pools, multiple threads can be reused.

2. Benefits of thread pool

We know that without a thread pool, each thread needs to create and run a thread in the way of new thread (xxrunnable). Start(). If there are fewer threads, this will not be a problem. However, in real environment, multiple threads may be opened to maximize the efficiency of the system and program,When the number of threads reaches a certain number, the CPU and memory resources of the system will be exhausted, and GC will collect and pause frequently, because each time a thread is created and destroyed, system resources will be consumed. If a thread is created for each task, it is undoubtedly a big performance bottleneck. Therefore, the reuse of threads in the thread pool greatly saves system resources. When a thread has no more tasks to process for a period of time, it will be destroyed automatically instead of staying in memory for a long time.

3. Thread pool core class–Detailed explanation of parameter flow

stay java.util.concurrent We can find the definition of thread pool in the packageThreadPoolExecutorIt’s our thread pool core class. First of all, let’s look at the main parameters of the thread pool class.

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default thread factory.
 */
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
}
  • Core pool size: the core size of the thread pool, which can also be understood as the minimum size of the thread pool.
  • Maximumpoolsize: maximum thread pool size.
  • Keepalivetime: the survival time of spare threads, which refers to how long it takes to destroy the spare threads that exceed the core pool size.
  • Unit: destroy time unit.
  • Workqueue: stores the work queue waiting for the thread to execute.
  • Threadfactory: the factory where threads are created. Generally, the default is used.
  • Handler: rejection policy. When the work queue and thread pool are full, how to reject new tasks and throw an exception by default.

4. Thread pool workflow

  • 1. If the number of threads in the thread pool is less than corepoolsize, a new thread is created to execute the task directly.
  • 2. If the number of threads in the thread pool is larger than the corepoolsize, the task will be temporarily stored in the workqueue for execution.
  • 3. If the workqueue is also full, when the number of threads is less than the maximum number of thread pools maximumpoolsize, a new thread will be created for processing, and when the number of threads is greater than or equal to the maximum number of thread pools maximumpoolsize, the rejection policy will be executed.

5. Thread pool classification

ExecutorsJDK is a factory class for creating thread pool. It provides four common thread pool applications by default, and we don’t have to construct them repeatedly.

  • newFixedThreadPool
    Fixed thread pool, the number of core threads and the maximum number of threads are fixed equal, and the idle survival time is 0 ms, which indicates that this parameter is meaningless, and the maximum number of work queue is 0 Ms Integer.MAX_ Blocking queue of value size. When executing tasks, if the threads are busy, they will be thrown to the work queue and executed when there are idle threads. If the queue is full, the default rejection policy will be executed.
 /**
  * Creates a thread pool that reuses a fixed number of threads
  * operating off a shared unbounded queue, using the provided
  * ThreadFactory to create new threads when needed. 
  */
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
}
  • newCachedThreadPool
    Buffered thread pool. From the perspective of structure, the number of core threads is 0, and the maximum number of threads is the maximum size of integer. If more than 0 idle threads are destroyed after 60 seconds, synchronousqueue is a queue submitted directly, which means that each new task will be executed by a thread. If there are available threads in the thread pool, the task will be executed. If there are no available threads in the thread pool, one will be created to execute The number of threads is uncertain. Generally, it is recommended to execute faster and smaller threads. Otherwise, the maximum thread pool boundary is too large, which is easy to cause memory overflow.
 /**
  * Creates a thread pool that creates new threads as needed, but
  * will reuse previously constructed threads when they are
  * available.  These pools will typically improve the performance
  * of programs that execute many short-lived asynchronous tasks.
  */
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
  • newSingleThreadExecutor
    In the single thread pool, the number of core threads and the maximum number of threads are both 1. It is also meaningless for idle threads to survive for 0 ms, which means that only one thread is executed at a time. The redundant threads are stored in the work queue and executed one by one, ensuring the sequential execution of threads.
/**
 * Creates an Executor that uses a single worker thread operating
 * off an unbounded queue. (Note however that if this single
 * thread terminates due to a failure during execution prior to
 * shutdown, a new one will take its place if needed to execute
 * subsequent tasks.)  Tasks are guaranteed to execute
 * sequentially, and no more than one task will be active at any
 * given time. Unlike the otherwise equivalent
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
  • newScheduledThreadPool
    Scheduling thread pool, that is, executing tasks according to a certain cycle, that is, timing tasks, just packaging ThreadPoolExecutor.
/**
 * Creates a thread pool that can schedule commands to run after a
 * given delay, or to execute periodically.
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

6. Rejection strategy

  • AbortPolicy
    It’s simple and rude, and it throws a rejection exception directly, which is also the default rejection strategy.
  • CallerRunsPolicy
    If the thread pool is not closed, new tasks will be executed directly in the caller thread, which will slow down the performance of the main thread submission thread.
  • DiscardPolicy
    From the method point of view, not doing task operation means not processing new tasks, that is, discarding them.
  • DiscardOldestPolicy
    To abandon the oldest task is to take the oldest task out of the queue and put it into a new task for execution.

7. Blocking queue

  1. Introduction to buffering queue

    BlockingQueue is a double buffered queue. BlockingQueue uses two queues internally, which allows two threads to store and retrieve to the queue at the same time. It improves the access efficiency of the queue while ensuring the concurrency security.
  2. There are several commonly used blockingqueues
  • Arrayblockingqueue (int i): a BlockingQueue of specified size whose construction must specify the size. The objects are sorted in FIFO order.
  • Linkedblockingqueue() or (int i): for a BlockingQueue with an unfixed size, if the size is specified during its construction, the generated BlockingQueue has a size limit. If the size is not specified, the size is limited Integer.MAX_ Value. The objects are sorted in FIFO order.
  • Priorityblockingqueue() or (int i): similar to linkedblockingqueue, but the sorting of its contained objects is not FIFO, but is determined by the natural order of the objects or the comparator of the constructor.
  • Synchronizedqueue(): a special BlockingQueue whose operation must be completed alternately by putting and fetching.

8. How to submit threads

You can define any fixed size thread pool first

ExecutorService es = Executors.newFixedThreadPool(3);

Submit a thread

es.submit(xxRunnble);
es.execute(xxRunnble);

What is the difference between submit and execute?

    1. Execute has no return value. If you use the execute method without knowing the result of the thread, the performance will be much better.
    1. Submit returns a future object. If you want to know the thread result, submit with submit. Moreover, it can catch the exception in the main thread through the get method of future.

Let’s see how the execute () method handles this

    1. Gets the state of the current thread pool.
    1. Create a new thread to run when the current number of threads is less than coresize.
    1. If the current thread is running and the write to the blocking queue is successful.
    1. If the thread state changes (non running state), you need to remove the task from the blocking queue, and try to judge whether all threads have been executed, and execute the rejection policy.
    1. If the current thread pool is empty, a new thread is created and executed.
    1. If the judgment in the third step is not running, try to create a new thread. If it fails, execute the rejection policy.

9. How to close thread pool

es.shutdown(); 

No longer accept new tasks, and close the thread pool after the execution of the previously submitted tasks.

es.shutdownNow();

No longer accept new tasks, try to stop the tasks in the pool, close the thread pool, and return the list of all unprocessed threads.

10. Summary

Thread pool is mainly used to solve the problem of thread life cycle overhead and resource shortage. By reusing threads for multiple tasks, the cost of thread creation is allocated to multiple tasks, and the thread already exists when the request arrives, so the delay caused by thread creation is eliminated. In this way, the request can be served immediately, making the application more responsive. In addition, by properly adjusting the number of threads in the thread, the resource shortage can be prevented.

Recommended Today

The use of springboot Ajax

Ajax overview What is Ajax? data Ajax application scenarios? project Commodity system. Evaluation system. Map system. ….. Ajax can only send and retrieve the necessary data to the server, and use JavaScript to process the response from the server on the client side. data But Ajax technology also has disadvantages, the biggest disadvantage is that […]