Defeating Java multithreading and high concurrency-thread pool articles

This article is a note made when learning Java multithreading and high concurrency knowledge.

This part has a lot of content and is divided into 5 parts according to the content:

  1. Multithreading basics
  2. JUC articles
  3. Synchronous container and concurrent container
  4. Thread Pool
  5. MQ articles

This article is a thread pool article.

table of Contents

1 Introduction to thread pools

2 Three kinds of thread pools

2.1 Single-threaded thread pool

2.2 Fixed-length thread pool

2.3 Cacheable thread pool

2.4 Matters needing attention

3 Seven parameters

3.1 Thread pool parameters

3.2 custom thread pool

4 Four rejection strategies

4.1 AbortPolicy

4.2 CallerRunsPolicy

4.3 DiscardPolicy

4.4 DiscardOldestPolicy


1 Introduction to thread pools

Thread pool: A mode of thread usage. Too many threads will bring scheduling overhead, which in turn affects cache locality and overall performance, and the thread pool maintains multiple threads, waiting for the supervisory manager to allocate tasks that can be executed concurrently. Using the thread pool avoids the cost of creating and destroying threads when processing short-term tasks. The number of threads in the thread pool should not be too much, otherwise it will cause additional thread switching overhead. Generally, the number of threads is more appropriate to take the number of CPUs + 2.

Advantages of using thread pool:

  • Reduce resource consumption: reduce the consumption caused by thread creation and destruction by reusing already created threads.
  • Improve response speed: When a task arrives, it can be executed immediately without waiting for the thread to be created.
  • Convenient thread management: uniform allocation, tuning and monitoring of threads.

That is, thread reuse, control the maximum concurrent number, and manage threads.

What you must know about thread pools is: three thread pools, seven parameters, and four rejection strategies.

 

2 Three kinds of thread pools

A tool class Executors is provided in the java.util.concurrent package.

The so-called three thread pools refer to the three thread pools defined in the java.util.concurrent.Executors class:

  • Single threaded thread pool
  • Fixed-length thread pool
  • Cacheable thread pool

2.1 Single-threaded thread pool

The number of threads in the single-threaded thread pool is 1, and all work tasks will be executed by this only thread.

Creation method:

ExecutorService threadPool = Executors.newSingleThreadExecutor();

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

public class Test {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newSingleThreadExecutor();
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.execute(() -> { //使用线程池来执行多线程任务
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

operation result:

pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok
pool-1-thread-1 ok

2.2 Fixed-length thread pool

The fixed-length thread pool can control the number of threads to control the maximum concurrent number of threads.

Creation method:

ExecutorService threadPool = Executors.newFixedThreadPool (maximum number of threads);

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

public class Test {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.execute(() -> { //使用线程池来执行多线程任务
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

operation result:

pool-1-thread-1 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-3 ok
pool-1-thread-1 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-1 ok

2.3 Cacheable thread pool

Cacheable thread pool, the thread pool adaptively adjusts the number of threads required by the task.

Adaptive: When the thread pool wants to open a new thread task, if there is an idle thread in the thread pool, the idle thread will handle the task, and if there is no idle thread, a new thread will be created.

Creation method:

ExecutorService threadPool = Executors.newCachedThreadPool();

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

public class Test {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        try {
            for (int i = 0; i < 10; i++) {
                threadPool.execute(() -> { //使用线程池来执行多线程任务
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

operation result:

pool-1-thread-1 ok
pool-1-thread-3 ok
pool-1-thread-2 ok
pool-1-thread-4 ok
pool-1-thread-2 ok
pool-1-thread-3 ok
pool-1-thread-6 ok
pool-1-thread-5 ok
pool-1-thread-1 ok
pool-1-thread-5 ok

2.4 Matters needing attention

It should be noted that in the "Alibaba Java Development Manual", [mandatory] requires that the thread pool is not allowed to use Executors to create , but through the ThreadPoolExecutor method (custom thread pool), this processing method makes the writing students more Clarify the operating rules of the thread pool to avoid the risk of resource exhaustion.

The disadvantages of the thread pool object returned by Executors are as follows:

  • FixedThreadPool and SingleThreadPool: The allowed request queue length is Integer.MAX_VALUE, which may accumulate a large number of requests, leading to OOM.
  • CachedThreadPool and ScheduledThreadPool: The allowed number of threads to be created is Integer.MAX_VALUE, which may create a large number of threads, resulting in OOM.

 

3 Seven parameters

3.1 Thread pool parameters

We try to analyze the source code of three thread pool creation methods:

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

It was found that they all returned a ThreadPoolExecutor object.

View the constructor of the ThreadPoolExecutor class:

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

You can see the seven parameters that define the thread pool:

  • int corePoolSize, core thread pool size.
  • int maximumPoolSize, the maximum thread pool size.
  • long keepAliveTime, keep alive time.
  • TimeUnit unit, the unit of time to remain active.
  • BlockingQueue<Runnable> workQueue, work (blocking) queue.
  • ThreadFactory threadFactory, thread factory.
  • RejectedExecutionHandler handler, refuse to execute the handler.

Next, let's talk about a model of a bank business hall, so that everyone can understand the meaning of each parameter in the thread pool.

There used to be a bank business hall, [Thread Pool]

This bank business hall has 5 windows, [maximum thread pool size=5]

When the business is idle, there are only two windows for business. [Core thread pool size=2]

There is a waiting area in the business hall with 3 seats. [Work (blocking) queue, size=3]

When business was busy, once,

Two customers came first, and they went to two frequently open windows to handle business as soon as they arrived.

Three more customers came. They found that there were people handling business at all open windows, and they were sitting in the waiting area waiting.

Another customer came. The business hall found that the open window and waiting area were no longer available, so they opened another window for business.

Two more customers came, all windows in the business hall were open for business, and the waiting area was full.

Another customer came, and the business hall refused to provide him with services. [Refuse to execute the processing procedure]

After the busy period of business has passed, the speed of customers' coming in can no longer catch up with the processing speed of all open windows.

There is a window where there is no customer to handle business for 1 hour, [keep active time=1, keep active time unit=hour]

The window is closed.

In the above model, the thread factory is not mentioned, it is used to create threads. 【Factory Mode】

The thread factory is used here to unify the setting of some parameters when creating a thread, and generally do not need to be modified.

Regarding the refusal execution process, we go to the chapter on four refusal strategies to further explain.

3.2 custom thread pool

After understanding the seven parameters of the thread pool, we can create a custom thread pool by way of new ThreadPoolExecutor(,,,,,,). In the "Alibaba Java Development Manual", [Mandatory] requires the use of this method to create a thread pool.

 

Create a custom thread pool:

import java.util.concurrent.*;

public class BusinessHall {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2, //核心线程池大小=2
                5, //最大线程池大小=5
                2, //保持活跃时间=2
                TimeUnit.SECONDS, //保持活跃时间单位=秒
                new LinkedBlockingDeque<>(3), //工作(阻塞)队列,大小=3
                Executors.defaultThreadFactory(), //默认线程工厂
                new ThreadPoolExecutor.AbortPolicy() //一种拒绝执行处理程序
        );
        try {
            for (int i = 0; i < 8; i++) { //保证没有任务被拒绝执行的最大线程数=5+3
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

operation result:

pool-1-thread-1 ok
pool-1-thread-2 ok
pool-1-thread-3 ok
pool-1-thread-4 ok
pool-1-thread-1 ok
pool-1-thread-3 ok
pool-1-thread-5 ok
pool-1-thread-2 ok

When creating a custom thread pool during work, what is the appropriate maximum thread pool size?

First evaluate whether the active process is CPU-intensive or IO-intensive:

  • CPU-intensive: Most of the program's time is spent on calculations.
  • IO-intensive: Most of the program's time is spent on input and output.

If it is a CPU-intensive program, set the maximum thread pool size to the number of logical processors in the server.

API for obtaining the number of server logical processors:

public class Test {
    public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().availableProcessors());
    }
}

 If it is IO-intensive, evaluate how many threads in the program that consume IO resources, as long as it is larger than this number, it is ok, and it can be set to double.

 

4 Four rejection strategies

There are four rejection strategies for the thread pool rejection handler:

  • AbortPolicy, the default.
  • CallerRunsPolicy
  • DiscardPolicy
  • DiscardOldestPolicy

4.1 AbortPolicy

When the number of tasks exceeds the sum of the maximum thread pool size and the work (blocking) queue size, an exception is thrown:

java.util.concurrent.RejectedExecutionException

Code test: (may not report an error, test several times)

import java.util.concurrent.*;

public class BusinessHall {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy() //拒绝执行处理程序
        );
        try {
            for (int i = 0; i < 10; i++) { //任务数目超过最大线程池大小于工作(阻塞)队列大小之和
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

Operation throws an exception:

Exception in thread "main" java.util.concurrent.RejectedExecutionException

4.2 CallerRunsPolicy

When the number of tasks exceeds the sum of the maximum thread pool size and the size of the work (blocking) queue, the excess tasks are returned to the thread of the task source for execution.

Code test:

import java.util.concurrent.*;

public class BusinessHall {
    public static void main(String[] args) {
        ExecutorService threadPool = new ThreadPoolExecutor(2,
                5,
                2,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy() //拒绝执行处理程序
        );
        try {
            for (int i = 0; i < 10; i++) { //任务数目超过最大线程池大小于工作(阻塞)队列大小之和
                threadPool.execute(() -> {
                    System.out.println(Thread.currentThread().getName() + " ok");
                });
            }
        } finally {
            threadPool.shutdown(); //线程池一定要关闭
        }
    }
}

operation result:

pool-1-thread-1 ok
pool-1-thread-3 ok
main ok
pool-1-thread-4 ok
pool-1-thread-2 ok
pool-1-thread-2 ok
pool-1-thread-4 ok
pool-1-thread-5 ok
pool-1-thread-3 ok
pool-1-thread-1 ok

4.3 DiscardPolicy

When the number of tasks exceeds the sum of the maximum thread pool size and the work (blocking) queue size, the excess tasks are discarded. No exception will be thrown.

4.4 DiscardOldestPolicy

When the number of tasks exceeds the sum of the maximum thread pool size and the size of the work (blocking) queue, the oldest task in the work (blocking) queue is discarded, and the excess tasks enter the queue. No exception will be thrown.

 

Learning video link:

https://www.bilibili.com/video/BV1B7411L7tE

加油! (d • _ •) d

Guess you like

Origin blog.csdn.net/qq_42082161/article/details/114002353