[Multi-threaded high concurrency] Thread pool basic summary + thread pool classification + blocking queue classification + rejection strategy

 Classification of thread pools

The code part is in java\javase\code\thread_pool

1、ThreadPoolExecutor

  • newCachedThreadPool
    • Create a thread pool that can create new threads as needed, but will reuse them when previously constructed threads are available, and use the provided ThreadFactory to create new threads when needed

      feature:

      (1) The number of thread pools is not fixed and can reach the maximum value (Interger. MAX_VALUE) 
      (2) Threads in the thread pool can be cache reused and recycled (the default time for recycling is 1 minute) 
      (3) When in the thread pool, No threads are available, a thread will be recreated

    • public class CacheThreadPoolDemo {
      
          public static void main(String[] args) {
              ExecutorService executorService = Executors.newCachedThreadPool();
              for(int i = 0;i<20;i++){
                  executorService.execute(new Task());
              }
              executorService.shutdown();
          }
      
      }
      public class Task implements Runnable {
          @Override
          public void run() {
      //        try {
      //            Thread.sleep(1000);
      //        } catch (InterruptedException e) {
      //            e.printStackTrace();
      //        }
              System.out.println(Thread.currentThread().getName()+" running");
          }
      }

       

  • newFixedThreadPool
    • Create a reusable thread pool with a fixed number of threads, and run these threads in a shared unbounded queue. At any point, most nThreads threads will be active in processing tasks. If additional tasks are submitted when all threads are active, the additional tasks will wait in the queue before there are available threads. If any thread terminates due to a failure during the execution before shutdown, a new thread will replace it to perform subsequent tasks (if needed). Until a thread is explicitly shut down, the threads in the pool will always exist.

      feature:

      (1) The threads in the thread pool are in a certain amount, and the concurrency of threads can be well controlled. 
      (2) Threads can be used repeatedly and will always exist until the display is closed. 
      (3) Threads exceeding a certain amount are submitted Time to wait in the queue

    • public class FixedThreadPoolDemo {
          public static void main(String[] args) {
              ExecutorService executorService = Executors.newFixedThreadPool(5);
              for (int i = 0 ;i<20;i++){
                  executorService.execute(new Task());
              }
              executorService.shutdown();
          }
      }

       

  • newSingleThreadExecutor
    • Create an Executor using a single worker thread, and run the thread in an unbounded queue. (Note that if this single thread is terminated due to a failure during the execution before the shutdown, a new thread will replace it to perform subsequent tasks if necessary). It is guaranteed that each task will be executed sequentially, and no more than one thread will be active at any given time. Unlike other equivalent newFixedThreadPool(1), it can ensure that other threads can be used without reconfiguring the executor returned by this method.

      Features: 
      (1) At most 1 thread is executed in the thread pool, and the thread activities submitted afterwards will be queued for execution

    • public class SingleThreadPoolDemo {
          public static void main(String[] args) {
              ExecutorService executorService = Executors.newSingleThreadExecutor();
              for(int i = 0;i<20;i++){
                  executorService.execute(new Task());
              }
              executorService.shutdown();
          }
      }

       

ScheduledThreadPoolExecutor

  • newSingleThreadScheduledExecutor
    • Create a single-threaded execution program that can schedule commands to run after a given delay or execute them periodically.
      Features: 
      (1) At most 1 thread can be executed in the thread pool, and the thread activities submitted afterwards will be queued for execution 
      (2) Thread activities can be executed regularly or delayed
    •  

  • newScheduledThreadPool
    • Create a thread pool that can schedule commands to run after a given delay or execute them periodically.
      Features: 
      (1) There are a specified number of threads in the thread pool, even empty threads will remain 
      (2) The thread activities can be executed regularly or delayed
      ​
      public class ScheduledThreadPoolDemo2 {
          public static void main(String[] args) {
              ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
              System.out.println(System.currentTimeMillis());
              scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("1------延迟一秒执行,每三秒执行一次");
                      System.out.println(System.currentTimeMillis());
                  }
              },1,3, TimeUnit.SECONDS);
              scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("2------延迟一秒执行,每三秒执行一次");
                      System.out.println(System.currentTimeMillis());
                  }
              },1,3, TimeUnit.SECONDS);
              scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("3-------延迟一秒执行,每三秒执行一次");
                      System.out.println(System.currentTimeMillis());
                  }
              },1,3, TimeUnit.SECONDS);
              scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("4--------延迟一秒执行,每三秒执行一次");
                      System.out.println(System.currentTimeMillis());
                  }
              },1,3, TimeUnit.SECONDS);
      //        scheduledExecutorService.shutdown();
          }
      }
      
      ​

       

    • public class ScheduledThreadPoolDemo {
          public static void main(String[] args) {
              ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
              ExecutorService service = Executors.newCachedThreadPool();
              System.out.println(System.currentTimeMillis());
              scheduledExecutorService.schedule(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("延迟三秒执行");
                      System.out.println(System.currentTimeMillis());
                  }
              },3, TimeUnit.SECONDS);
              scheduledExecutorService.shutdown();
          }
      }

 

ForkJoinPool

  • newWorkStealingPool
    • Create a thread pool with a parallel level. The parallel level determines the maximum number of threads in execution at the same time. If the parallel level parameter is not passed, it will default to the number of CPUs in the current system.
    • public class newWorkStealingPoolTest {
       
      	public static void main(String[] args) throws Exception {
       
      		// 设置并行级别为2,即默认每时每刻只有2个线程同时执行
      		ExecutorService m = Executors.newWorkStealingPool();
       
      		for (int i = 1; i <= 10; i++) {
      			final int count=i;
      			m.submit(new Runnable() {
      				@Override
      				public void run() {
      					Date now=new Date();
      					System.out.println("线程" + Thread.currentThread() + "完成任务:"
      							+ count+"   时间为:"+	now.getSeconds());
      					try {
      						Thread.sleep(1000);//此任务耗时1s
      					} catch (InterruptedException e) {
      						e.printStackTrace();
      					}
      				}
       
      			});
                 
      		}
      		while(true){
      			//主线程陷入死循环,来观察结果,否则是看不到结果的
      		}
      	}
      }
      public class ForJoinPollTask {
      
          public static void main(String[] args) throws Exception {
              int[] arr = new int[100];
              Random random = new Random();
              int total =0;
              //初始化100个数组元素
              for(int i=0,len = arr.length;i<len;i++){
                  int temp = random.nextInt(20);
                  //对数组元素赋值,并将数组元素的值添加到sum总和中
                  total += (arr[i]=temp);
              }
              System.out.println("初始化数组总和:"+total);
              SumTask task = new SumTask(arr, 0, arr.length);
      //        创建一个通用池,这个是jdk1.8提供的功能
              ForkJoinPool pool = ForkJoinPool.commonPool();
              Future<Integer> future = pool.submit(task); //提交分解的SumTask 任务
              System.out.println("多线程执行结果:"+future.get());
              pool.shutdown(); //关闭线程池
              
              
      
          }
      
      }
      public class ForkJoinPoolAction {
          
          public static void main(String[] args) throws Exception{
              // 300大于50才会拆
              PrintTask task = new PrintTask(0, 300);
              //创建实例,并执行分割任务
              ForkJoinPool pool = new ForkJoinPool();
              pool.submit(task);
               //线程阻塞,等待所有任务完成
              pool.awaitTermination(2, TimeUnit.SECONDS);
              pool.shutdown();
          }
      }
      class PrintTask extends RecursiveAction {
          private static final int THRESHOLD = 50; //最多只能打印50个数
          private int start;
          private int end;
      
      
      
          public PrintTask(int start, int end) {
              super();
              this.start = start;
              this.end = end;
          }
      
      
      
          @Override
          protected void compute() {
      
              if(end - start < THRESHOLD){
                  for(int i=start;i<end;i++){
                      System.out.println(Thread.currentThread().getName()+"的i值:"+i);
                  }
              }else {
                  int middle =(start+end)/2;
                  PrintTask left = new PrintTask(start, middle);
                  PrintTask right = new PrintTask(middle, end);
                  //并行执行两个“小任务”
                  left.fork();
                  right.fork();
              }
      
          }
      
      }
      class SumTask extends RecursiveTask<Integer> {
          private static final int THRESHOLD = 20; //每个小任务 最多只累加20个数
          private int arry[];
          private int start;
          private int end;
      
      
      
          /**
           * Creates a new instance of SumTask.
           * 累加从start到end的arry数组
           * @param arry
           * @param start
           * @param end
           */
          public SumTask(int[] arry, int start, int end) {
              super();
              this.arry = arry;
              this.start = start;
              this.end = end;
          }
      
      
      
          @Override
          protected Integer compute() {
              int sum =0;
              //当end与start之间的差小于threshold时,开始进行实际的累加
              if(end - start <THRESHOLD){
                  for(int i= start;i<end;i++){
                      System.out.println(Thread.currentThread().getName()+"的i值:"+arry[i]);
                      sum += arry[i];
                  }
                  return sum;
              }else {//当end与start之间的差大于threshold,即要累加的数超过20个时候,将大任务分解成小任务
                  int middle = (start+ end)/2;
                  SumTask left = new SumTask(arry, start, middle);
                  SumTask right = new SumTask(arry, middle, end);
                  //并行执行两个 小任务
                  left.fork();
                  right.fork();
                  //把两个小任务累加的结果合并起来
                  return left.join()+right.join();
              }
      
          }
      
      }

       

Classification of blocking queues

BlockingQueue

  • ArrayBlockingQueue
    • Based blocking queue array implemented inside ArrayBlockingQueue, maintains a fixed-length array , for buffering data object queue, which is a commonly used blocking queue, in addition to a fixed-length array, the internal ArrayBlockingQueue also holds two integer variables , Respectively identify the position of the head and tail of the queue in the array.
        ArrayBlockingQueue shares the same lock object when the producer puts the data and the consumer gets the data, which also means that the two cannot run in parallel. This is especially different from LinkedBlockingQueue. According to the analysis of the implementation principle, ArrayBlockingQueue can be used. Separate the lock, so as to realize the complete parallel operation of producer and consumer operations. The reason why Doug Lea did not do this may be because the data writing and obtaining operations of ArrayBlockingQueue are already light enough that the introduction of an independent lock mechanism, in addition to the additional complexity to the code, does not take up the performance at all. To any cheaper. Another obvious difference between ArrayBlockingQueue and LinkedBlockingQueue is that the former will not generate or destroy any additional object instances when inserting or deleting elements, while the latter will generate an additional Node object. In a system that needs to process large quantities of data efficiently and concurrently for a long time, there are still certain differences in its impact on GC. When creating an ArrayBlockingQueue, we can also control whether the internal lock of the object uses a fair lock, and unfair locks are used by default.
  • LinkedBlockingQueue
    • The blocking queue based on the linked list is similar to the ArrayListBlockingQueue. It also maintains a data buffer queue (the queue is composed of a linked list). When the producer puts a data into the queue, the queue will get the data from the producer, and Cached inside the queue, and the producer returns immediately; only when the queue buffer reaches the maximum buffer capacity (LinkedBlockingQueue can specify this value through the constructor), the producer queue will be blocked until the consumer consumes a copy from the queue Data, the producer thread will be awakened, and the processing on the consumer side is based on the same principle. The LinkedBlockingQueue can efficiently process concurrent data because it uses independent locks for the producer and consumer to control data synchronization. This also means that the producer and consumer can be parallel in the case of high concurrency. Operate the data in the queue to improve the concurrency performance of the entire queue.
  • DelayQueue
    • The element in DelayQueue can only get the element from the queue when the specified delay time has elapsed . DelayQueue is a queue with no size limit , so the operation of inserting data into the queue ( producer) will never be blocked , and only the operation of obtaining data ( consumer) will be blocked .
        Usage scenarios:
        DelayQueue uses fewer scenarios, but they are all quite clever. A common example is using a DelayQueue to manage a connection queue that has not responded to a timeout.
  • PriorityBlockingQueue
    • Based on the priority of blocking queue (priority judgment passed by the constructor Compator object to the decision), but it should be noted that PriorityBlockingQueue and does not block data producers, but only when there is no available data consumption, blocking data Of consumers. Therefore, you must pay special attention when using it. The data produced by the producer must not be faster than the data consumed by the consumer. Otherwise, over time, it will eventually exhaust all the available heap memory space. When implementing PriorityBlockingQueue, the internal control thread synchronization lock uses a fair lock.
  • SynchronousQueue
    • An unbuffered waiting queue, similar to direct transactions without intermediaries , a bit like producers and consumers in primitive society. Producers take their products to the market and sell them to the final consumers of the products, and consumers must go in person The bazaar finds the direct producer of the desired commodity. If one party fails to find a suitable target, then I’m sorry, everyone is waiting at the bazaar. Compared with the buffered BlockingQueue, there is no intermediate distributor link (buffer). If there is a distributor, the producer directly wholesales the products to the distributor without worrying that the distributor will eventually sell these products to those Consumers, because dealers can stock part of the goods, compared with the direct transaction model, the overall throughput of the intermediate dealer model is higher (can be sold in batches); but on the other hand, because of the introduction of dealers, As a result, an additional transaction link is added from the producer to the consumer, and the timely response performance of a single product may be reduced .
           There are two different ways to declare a SynchronousQueue, and they have different behaviors. The difference between fair mode and unfair mode :
        If fair mode is adopted: SynchronousQueue will use fair lock and cooperate with a FIFO queue to block redundant producers and consumers , thus the overall fairness strategy of the system;
        but if it is unfair mode (SynchronousQueue default ): SynchronousQueue uses unfair locks and cooperates with a LIFO queue to manage redundant producers and consumers . In the latter mode, if there is a gap between the processing speeds of producers and consumers, it is easy to become hungry, that is There may be some producers or consumers whose data will never be processed.

 

The difference between arrayblockingqueue and linkedblockqueue:

1. The realization of locks in the queue. The locks in the queue implemented by ArrayBlockingQueue are not separated, that is, the same lock is used for production and consumption; the locks in the queue implemented by LinkedBlockingQueue are separated, that is, putLock is used for production and putLock is used for consumption. It is takeLock
2. The queue size initialization method is different. The queue size must be specified in the queue implemented by ArrayBlockingQueue; the queue size may not be specified in the queue implemented by LinkedBlockingQueue, but the default is Integer.MAX_VALUE 

The life cycle of the thread pool

(Please distinguish between thread pool and thread life cycle)

  • RUNNING, which means that new tasks can be accepted and tasks in the queue can be executed;
  • SHUTDOWN, closed state, which means that new tasks are not accepted, but tasks in the queue can be executed;
  • STOP means that new tasks are not accepted, and tasks in the queue are no longer executed, and tasks that are being executed are interrupted;
  • TIDYING, all tasks have been suspended, and the number of workerCount (number of effective threads) worker threads is 0, and finally the thread that changes to this state will execute the terminated() hook method and enter the TERMINATED state, and only one thread will execute this method;
  • TERMINATED, aborted state, the terminated() hook method has been executed, and nothing is in uo by default in the terminated() method;

Rejection strategy

  • ThreadPoolExecutor.AbortPolicy: Discard the task and throw a RejectedExecutionTxception exception. By analogy, a girl confessed to a boy, but a boy refused directly, comparable to a straight man. (Because there are anomalies, it is easier to adjust the common anomaly detection mechanism)
  • ThreadPoolExecutor.DiscardPolicy: also discards tasks, but does not throw exceptions. By analogy, girls confess to boys, boys do not respond, hang up, do not accept or object.
  • ThreadPoolExecutor.DiscardOldestPolicy: Discard the first task in the queue, and then try to execute the task again (repeat this process). By analogy, the two have been in love for many years. Suddenly Bai Fumi confessed to the man. The man abandoned his ex-girlfriend and chose to be with Bai Fumi.
  • ThreadPoolExecutor.CallerRunsPolicy: The task is handled by the calling thread. By analogy, many girls pursue boys, female number one, number two, number three and so on. Suddenly a girl who has never met before (not in the queue) and the boy are with him.

It is the first one used by default, and it is also the first and most used in actual projects. The analogy is annoying but very practical!

 

 

 

 

Guess you like

Origin blog.csdn.net/zw764987243/article/details/110381598