What is the JAVA thread pool, what benefits can it bring, what are the thread blocking queues and how to use them, what is the saturation strategy, and what is the operating mechanism

What is a thread pool

As the name suggests, the thread pool is to create several executable threads in advance and put them into a pool (container). When needed, threads can be obtained from the pool without creating them by themselves. After use, the threads do not need to be destroyed but put back into the pool, thereby reducing creation. and the overhead of destroying thread objects

Benefits of the thread pool

  1. Reduce resource consumption
  2. increase the corresponding speed
  3. Improve thread manageability

Optional blocking queue for thread pool

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;

Array-based bounded blocking queue

 @Test
    public void arrayBlockingQueue() throws InterruptedException {
    
    
        /**
         * 基于数组的有界阻塞队列,队列容量为10
         */
        ArrayBlockingQueue queue =
                new ArrayBlockingQueue<Integer>(10);

        // 循环向队列添加元素
        for (int i = 0; i < 20; i++) {
    
    
            queue.put(i);
            System.out.println("向队列中添加值:" + i);
        }
    }

Bounded/unbounded blocking queue based on linked list

    @Test
    public void linkedBlockingQueue() throws InterruptedException {
    
    
        /**
         * 基于链表的有界/无界阻塞队列,队列容量为10
         */
        LinkedBlockingQueue queue =
                new LinkedBlockingQueue<Integer>();

        // 循环向队列添加元素
        for (int i = 0; i < 20; i++) {
    
    
            queue.put(i);
            System.out.println("向队列中添加值:" + i);
        }
    }

Synchronous Handover Blocking Queue

(Direct submission) If you do not want the task to wait in the queue but want to hand over the task directly to the worker thread, you can use SynchronousQueue as the waiting queue. SynchronousQueue is not a real queue, but a mechanism for handover between threads. To put an element into a SynchronousQueue, another thread must be waiting to receive the element. It is recommended to use this queue only when using an unbounded thread pool or with a saturation strategy.

The default option for work queues is SynchronousQueue, a strategy that avoids locks when processing sets of requests that may have internal dependencies.

The characteristics of the Queue itself, after an element is added, it must wait for other threads to take it away before continuing to add.

@Test
    public void test() throws InterruptedException {
    
    
        /**
         * 同步移交阻塞队列
         */
        SynchronousQueue queue = new SynchronousQueue<Integer>();

        // 插入值
        new Thread(() -> {
    
    
            try {
    
    
                queue.put(1);
                System.out.println("插入成功");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }).start();

        // 删除值

        new Thread(() -> {
    
    
            try {
    
    
                queue.take();
                System.out.println("删除成功");
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        }).start();


        Thread.sleep(1000L * 60);
    }

Optional saturation strategy for thread pool

  • AbortPolicy termination policy (default)
  • DiscardPolicy discard policy
  • DiscardOldestPolicy discards the old task policy
  • CallerRunsPolicy caller run policy

Execution diagram of thread pool

insert image description here

  1. The main thread executes execute, submits tasks to the core thread pool, and the core thread pool processes or adds core threads
  2. If the core thread pool is full, the task will be submitted to the blocking queue, and the core thread will poll the consumption blocking queue
  3. When the blocking queue is full, the task will be submitted to the largest thread pool for non-core threads to process
  4. Non-core threads are also increased to the maximum thread pool, which will cause a saturation strategy

insert image description here

Common thread pool

NewCachedThreadPool of commonly used thread pool

/**
     * 线程数量无限的线程池(核心线程为0,最大线程数是无限大的值,默认的阻塞队列是同步移交策略,意味着有一个任务就有有一个线程去消费,然后去接受另一个任务,这个线程池会创建无数个线程最终系统崩溃)
     *
     * @return
     */
     
public static ExecutorService newCachedThreadPool() {
    
    
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
    }

NewFixedThreadPool of commonly used thread pool

    /**
     * 线程数量固定线程池(线程个数虽然固定了,但是无界的队列是没有限制的,任务队列也是会把内存挤爆的)
     * @param nThreads
     * @return
     */
     
    public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
        return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
    }

NewSingleThreadExecutor of commonly used thread pool

    /**
     * 单一线程池
     */
    public static ExecutorService newSingleThreadExecutor() {
    
    
        return new Executors.FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                        0L, TimeUnit.MILLISECONDS,
                        new LinkedBlockingQueue<Runnable>()));
    }

Submit tasks to the thread pool

import org.junit.Test;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class RunTest {
    
    

    @Test
    public void submitTest()
            throws ExecutionException, InterruptedException {
    
    

        // 创建线程池
        ExecutorService threadPool =
                Executors.newCachedThreadPool();

        /**
         * 利用submit方法提交任务,接收任务的返回结果
         */
        Future<Integer> future = threadPool.submit(() -> {
    
    
            Thread.sleep(1000L * 10);

            return 2 * 5;
        });

        /**
         * 阻塞方法,直到任务有返回值后,才向下执行
         */
        Integer num = future.get();

        System.out.println("执行结果:" + num);
    }

    @Test
    public void executeTest() throws InterruptedException {
    
    
        // 创建线程池
        ExecutorService threadPool =
                Executors.newCachedThreadPool();

        /**
         * 利用execute方法提交任务,没有返回结果
         */
        threadPool.execute(() -> {
    
    
            try {
    
    
                Thread.sleep(1000L * 10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            Integer num = 2 * 5;
            System.out.println("执行结果:" + num);
        });



        Thread.sleep(1000L * 1000);
    }

}

state of the thread pool

insert image description here

Four saturation strategies for thread pool saturation and code debugging

Define the thread pool

/**
     * 线程池
     */
    private static ThreadPoolExecutor executor =
            new ThreadPoolExecutor(
                    // 核心线程数和最大线程数
                    2, 3,

                    // 线程空闲后的存活时间
                    60L, TimeUnit.SECONDS,

                    // 有界阻塞队列
                    new LinkedBlockingQueue<Runnable>(5)
           );

Rewrite thread execution

    /**
     * 任务
     */
    class Task implements Runnable {
    
    
        /**
         * 任务名称
         */
        private String taskName;

        public Task(String taskName) {
    
    
            this.taskName = taskName;
        }

        @Override
        public void run() {
    
    
            System.out.println("线程[ " + Thread.currentThread().getName()
                    + " ]正在执行[ " + this.taskName + " ]任务...");

            try {
    
    
                Thread.sleep(1000L * 5);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }

            System.out.println("线程[ " + Thread.currentThread().getName()
                    + " ]已执行完[ " + this.taskName + " ]任务!!!");
        }
    }
    

termination strategy

demo

    /**
     * 终止策略
     * TODO 抛出异常,拒绝任务提交
     */
    @Test
    public void abortPolicyTest() {
    
    
        // 设置饱和策略为 终止策略
        executor.setRejectedExecutionHandler(
                new ThreadPoolExecutor.AbortPolicy());

        for (int i = 1; i <= 10; i++) {
    
    
            try {
    
    
                // 提交10个线程任务
                executor.execute(new Task("线程任务" + i));
            } catch (Exception e) {
    
    
                System.err.println(e);
            }
        }

        // 关闭线程池
        executor.shutdown();
    }
    

Results of the

insert image description here

Result description (execution process of thread pool)

  1. 2 tasks enter the core thread
  2. The 3rd to 7th tasks will be temporarily stored in the task queue, because the bounded queue is defined as 5
  3. The eighth task will start the largest thread to execute
  4. For the 9th and 10th tasks, there is no thread to execute, and it is terminated and thrown

throw away strategy

demo

   /**
    * 抛弃策略
    * TODO 直接丢弃掉新提交的任务
    */
   @Test
   public void discardPolicyTest() {
    
    
       // 设置饱和策略为 抛弃策略
       executor.setRejectedExecutionHandler(
               new ThreadPoolExecutor.DiscardPolicy());

       for (int i = 1; i <= 10; i++) {
    
    
           try {
    
    
               // 提交10个线程任务
               executor.execute(new Task("线程任务" + i));
           } catch (Exception e) {
    
    
               System.err.println(e);
           }
       }

       // 关闭线程池
       executor.shutdown();
   }

Results of the

insert image description here

Result description

Thread tasks No. 9 and No. 10 are directly discarded because they are full

Abandon the old mission strategy

demo

   /**
    * 抛弃旧任务策略
    * TODO 丢弃掉任务队列中的旧任务,暂存新提交的任务
    */
   @Test
   public void discardOldestPolicyTest() {
    
    
       // 设置饱和策略为 抛弃旧任务策略
       executor.setRejectedExecutionHandler(
               new ThreadPoolExecutor.DiscardOldestPolicy());

       for (int i = 1; i <= 10; i++) {
    
    
           try {
    
    
               // 提交10个线程任务
               executor.execute(new Task("线程任务" + i));
           } catch (Exception e) {
    
    
               System.err.println(e);
           }
       }

       // 关闭线程池
       executor.shutdown();
   }

Results of the

insert image description here

Result description

Thread 3 and thread 4 enter the priority queue and wait, and are also the first to be abandoned

caller run policy

demo

   /**
    * 调用者运行策略
    * TODO 借用主线程来执行多余任务
    */
   @Test
   public void callerRunsPolicyTest() {
    
    
       // 设置饱和策略为 调用者运行策略
       executor.setRejectedExecutionHandler(
               new ThreadPoolExecutor.CallerRunsPolicy());

       for (int i = 1; i <= 10; i++) {
    
    
           try {
    
    
               // 提交10个线程任务
               executor.execute(new Task("线程任务" + i));
           } catch (Exception e) {
    
    
               System.err.println(e);
           }
       }

       // 关闭线程池
       executor.shutdown();
   }

Results of the

insert image description here

Result description

Since thread tasks 9 and 10 are full, let the main process execute. This is the caller's running strategy

Guess you like

Origin blog.csdn.net/zhang5207892/article/details/109197337