Has the thread pool been used?

Keywords: less Java JDK

Has the thread pool been used?

Thread pool advantage

  • The main task of thread pool is to control the number of running threads, put tasks into the queue in the process of processing, and then start these tasks after thread creation. If the number of threads exceeds the maximum number of threads, wait for other threads to finish executing, and then take out the tasks from the queue to perform.
  • Main features: thread reuse, maximum concurrency control, thread management.
    • Reduce resource consumption. Reduce the consumption of thread creation and destruction by reusing created threads.
    • Increase response speed. When a task arrives, it can be executed immediately without waiting for the thread to be created.
    • Provides thread manageability. Threads are scarce resources. If created unrestrictedly, it will not only consume system resources, but also reduce the stability of the system. Thread pool can be used for uniform allocation, tuning and monitoring.

How to use it

  • Architectural description
    Thread pool in Java is implemented through Executor framework, which uses Executor, Executors, Executor Service and ThreadPool Executor.
  • Three Common Ways of Thread Pool
/**
 * Understanding:
 * Executors.newScheduledThreadPool()
 * java8 New out: Executors. newWork Stealing Pool (int) - Use processors available on current machines as its parallel level
 *
 * @author chenxiaonuo
 * @date 2019-08-16 16:34
 */
public class ThreadPoolDemo {

    public static void main(String[] args) {
        //Executor Service executor Service = Executors. new Fixed ThreadPool (5); //Fixed pools of five threads
        //ExecutorService executor Service = Executors. newSingleThreadExecutor ();//a thread pool
        ExecutorService executorService = Executors.newCachedThreadPool();//Pool of N threads

        //Simulate 10 users to handle business
        try {
            for (int i = 0; i < 20; i++) {
                executorService.execute(()->{
                    System.out.println(Thread.currentThread().getName() + "  Handle the business!");
                });
            }
        } catch (Exception e){
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

public class Executors{
   
   /*
       1.Create a fixed-length thread pool to control the maximum number of concurrent threads, and the threads that exceed it will wait in the queue.
       2.The created thread pool corePoolSize and maximumPoolSize values are equal, and it uses LinkedBlockingQueue
       3.Performing long-term tasks is much better
   */
   public static ExecutorService newFixedThreadPool(int nThreads) {
           return new ThreadPoolExecutor(nThreads, nThreads,
                                         0L, TimeUnit.MILLISECONDS,
                                         new LinkedBlockingQueue<Runnable>());
   }
   
   /*
       1.Create a single-threaded thread pool, which only uses a single worker thread to perform tasks, ensuring that all tasks are executed in accordance with the specified message.
       2.Set both corePoolSize and maximumPoolSize to 1, which uses LinkedBlockingQueue
       3.A scenario of task-by-task execution
    */
   public static ExecutorService newSingleThreadExecutor() {
           return new FinalizableDelegatedExecutorService
               (new ThreadPoolExecutor(1, 1,
                                       0L, TimeUnit.MILLISECONDS,
                                       new LinkedBlockingQueue<Runnable>()));
   }
   
   /*
       1.Create a cacheable thread pool that can flexibly reclaim idle threads if the length of the thread pool exceeds the processing requirement. If there is no reclaimable thread, create a new thread.
       2.Set corePoolSize to 0 and maximumPoolSize to Integer.MAX_VALUE, using SynchronousQueue, that is to say
           When a task arrives, it creates a thread to run, and when the thread is idle for more than 60 seconds, it destroys the thread.
       3.Execute many short-term asynchronous applets or lightly loaded servers
    */
   public static ExecutorService newCachedThreadPool() {
           return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                         60L, TimeUnit.SECONDS,
                                         new SynchronousQueue<Runnable>());
   }
   
}

Introduction to Several Important Parameters of Thread Pool

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
parameter describe Remarks
corePoolSize Number of Permanent Core Threads in Thread Pool 1. When a thread pool is created, when a task comes, the thread in the pool will be arranged to perform the request task; 2. When the number of threads in the thread pool reaches corePoolSize, the arrived task will be placed in the cache queue.
maximumPoolSize Thread pool can accommodate the maximum number of threads executed simultaneously, which must be greater than or equal to 1
keepAliveTime Survival time of spare threads When the current number of thread pools exceeds corePoolSize, when the idle time reaches keepAliveTime, the remaining idle threads will be destroyed until only corePoolSize threads are left.
unit The unit of keepAliveTime
workQueue Task queue, tasks submitted but not yet executed
threadFactory Represents the thread factory that generates the worker threads in the thread pool. The default is usually used to create threads.
handler Denial policy, which indicates how to deny runnable when the queue is full and the worker thread is larger than or equal to the maximum number of threads in the thread pool

Bottom Working Principle

  1. After creating the thread pool, wait for the submitted task request.
  2. When an execute() method is called to add a request task, the thread pool makes the following judgment:
    2.1 If the number of threads running is less than corePoolSize, create a thread to run the task immediately.
    2.2 If the number of threads running is greater than or equal to corePoolSize, add this task to the queue.
    2.3 If the queue is full and the number of threads running is less than maximumPoolSize, create non-core threads to run the task immediately.
    2.4 If the queue is full and the number of threads running is greater than or equal to maximumPoolSize, the thread pool will start the saturation rejection policy to execute.
  3. When a thread completes a task, it takes the next task from the queue to execute.
  4. When a thread has nothing to do for more than a certain period of time, the thread pool determines:
    • If the number of threads currently running is greater than corePoolSize, the thread will be stopped.
    • When all tasks in all thread pools are completed, it eventually shrinks to the size of corePoolSize.

Thread pool rejection strategy

At the same time, the max thread in the thread pool has reached and can not continue to serve the new task. At this time, we need to reject the strategy mechanism to deal with this problem reasonably.

  • JDK built-in rejection policy (both implements the Rejected Execution Handler interface)
    • AbortPolicy (default): Throw Rejected Execution Exception directly. The system is running normally.
    • CallerRunsPolicy: A mediation mechanism called "Caller Running", which neither abandons tasks nor throws exceptions, but returns certain tasks back to the caller, thereby reducing the flow of new tasks.
    • Discard Oldest Policy: Discard the longest-awaited task in the queue, then add the current task to the queue and try to resubmit the current task.
    • DiscardPolicy: Discard tasks directly without any processing or exception. This is the best way to allow tasks to be lost.

How to Set Reasonable Parameters in Production

  • CPU-intensive
    • This type of task requires a lot of computation, and without blocking, the CPU has been running at full speed. CPU-intensive tasks can only be accelerated (through multithreading) on real multicore CPUs.
    • General formula: CPU core + 1 thread pool
  • IO-intensive
    • Such task threads are not always executing tasks, so you should configure as many threads as possible, such as CPU core *2
    • This type of task requires a lot of IO, that is, a lot of blocking. Running IO-intensive tasks on a single thread can result in wasting a lot of CPU computing power and waiting. So using multithreading in IO-intensive tasks can greatly speed up program running, even on single-core CPUs, which mainly takes advantage of wasted blocking time.
    • When IO is intensive, most threads are blocked, so we need to configure more threads. Reference formula: CPU core /(1-blocking coefficient), blocking coefficient is between 0.8 and 0.9, such as 8-core CPU: 8/(1-0.9) = 80 threads.

Posted by Niyati_1984 on Mon, 19 Aug 2019 01:28:30 -0700