Concurrent programming (2): thread pool

  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

    

 

      

 

  

   

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325068095&siteId=291194637