Some things you need to know about the thread pool (1)

The article was first published on Youjian blog, everyone is welcome to visit Youjian blog

Introduction to thread pools

Before introducing the thread pool, let's first understand why we need to use multithreading . For example, when we go home from school every day, we have to do homework, and there are several homework such as: Chinese, mathematics, English... if we do one by one To write, you can only write mathematics after writing Chinese... Then it is probably at night after writing. This is the case of single-threaded single-core execution. If we use multi-threading, it becomes that we need to write several at the same time For homework, write a little bit of Chinese, then a little bit of mathematics, and then return to Chinese to continue writing, plus the fact that the homework is exchanged, the efficiency may be slower than the single-threaded time. This is the case of multi-threaded single-core execution. . If it is multi-core, it means that we have the function of a clone. While writing Chinese, another clone will write mathematics for us. When we finish writing Chinese, the mathematics will be finished. This is the case of multiple threads and multiple cores.

So if we want to deal with some intractable diseases one after another, we can call multi-threading for processing. Is the more threads the better? The more the faster? This is definitely negative. How many threads need to be created for a specific task to perform the best? How to control these threads to prevent multiple creation? …So, we can control the threads through the thread pool, as well as some other functions, we will introduce them in detail.
ps: From different perspectives, everything has two sides. For example, due to thread context switching, will single-core and multi-threading be slower than single-threaded...not necessarily, for example, an event needs to be read by IO first, and then CPU Call, but the cpu speed is too fast, and the IO speed is too slow, causing the cpu to be in a waiting state for a certain period of time, so we use multi-threading to process the next event while waiting for the IO data to be read in Event, reasonable use of cpu resources. Therefore, specific problems need to be analyzed in detail.

Benefits of thread pool

The thread pool creates several threads to respond to processing when the program starts. The threads in the thread pool are called worker threads, which have the following advantages
  :   First: reduce resource consumption. Reduce the consumption caused by thread creation and destruction by reusing the created threads.
  Second: Improve response speed. When the task arrives, the task can be executed immediately without waiting for the thread to be created.
  Third: Improve the manageability of threads.
Common thread pools are:
  Executors.newSingleThreadExecutor()
  Executors.newFixedThreadPool()
  Executors.newCachedTheadPool()
However, it is recommended to create a thread pool according to the actual situation of the business during development.

Introduction to thread pool

Ok, so much foreshadowing, we start from Executors step by step in-depth understanding of the thread pool.

//我们从这个方法开始看,看看Executors类是怎么做的
ExecutorService executorService = Executors.newCachedThreadPool();
//将这个类以及部分的方法进行展示,我们可以发现,不管是newCacheThreadPool还是别的线程池
//都是通过ThreadPoolExecutor()构造函数进行创建,只是传入的参数不同,从而导致了不同的线程池的产生
//我们先去对ThreadPoolExecutor这个类进行解析,再回来分析这些常用线程池的意义是什么。
public class Executors {
    
    
	
    public static ExecutorService newCachedThreadPool() {
    
    
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
	....
    public static ExecutorService newFixedThreadPool(int nThreads) {
    
    
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
	....
    public static ExecutorService newSingleThreadExecutor() {
    
    
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
}

Introduction to ThreadPoolExecutor

According to the above call, we can basically know that the thread pool is generated by the ThreadPoolExecutor class, and different thread pools are generated according to different parameter values.
Let's take a look at ThreadPoolExecutor and how its top layer is composed

//ThreadPoolExecutor继承了AbstractExecutorService类,获得它的一些方法以及实现
//点开AbstractExecutorService可以看见他还被很多其他的线程池所继承
public class ThreadPoolExecutor extends AbstractExecutorService{
    
    }

//AbstractExecutorService实现了ExecutorService接口,并对内部的方法进行实现
//ExecutorService接口内定义了许多线程池的基础方法,如shutdown(), shutdownNow(),submit()..等
public abstract class AbstractExecutorService implements ExecutorService {
    
    }

//ExecutorService 继承了 Executor接口
//Executor接口内部只有一个最核心的执行方法,void execute(Runnable command); 
public interface ExecutorService extends Executor {
    
    }

We focus on the specific implementation of ThreadPoolExecutor , starting with the construction method. To defeat the enemy, the first goal is to understand the enemy.

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters. 使用给定的初始化参数创建一个线程池
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * 		  设置了线程池的核心池大小,在不超过核心池大小的时候每有一个执行请求都会创建一个新的线程执行
     * 		  并且核心池的线程不会超时过期,除非设置allowCoreThreadTimeOut为true。
     * 	 	  设置为true后,核心池的线程在达到keepAliveTime后也会销毁
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * 		  线程池中的允许存在的最大线程数
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * 		  当线程数大于核心池线程数的时候,这是多出来的线程在没有任务时存活的最大
     * 		  时间
     * @param unit the time unit for the {@code keepAliveTime} argument
     * 		  上述存活时间的时间单位
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * 		  工作队列是在执行任务前保留任务的一个队列,这个队列将保留由execute提交的Runnable任务
     * 		  是一种用于缓存的任务的排队策略
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * 		  线程工厂是执行程序创建线程时所需要的创建工厂
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * 		  由于达到了线程池的最大值上限与队列上限,所执行的拒绝策略,
     * 		  在执行被阻止时使用的处理程序
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
    
    
         //首先是先判断创建的核心池大小 >= 0, 并且线程池的最大线程数要 > 0,不然无法创建线程就会
         //执行拒绝策略,其次是核心池的大小需要<=最大线程数,以及非核心池的空闲线程持续时间不能< 0
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
         //接着是对工作队列,线程工厂,以及拒绝策略的限定,不能为null。
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        //然后以下就是将传入的一些对应值进行赋值。
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

The above are some parameters that define the thread pool, and then look at some important parameter information defined when ThreadPoolExecutor is initialized and defined.

		//在后续的线程池执行中总会出现这个语句,这个很重要,定义了当前线程池的状态和活跃线程数
		//一个值就定义了两个状态
        int c = ctl.get();

int c = ctl.get(); Get the status value of the current thread and the number of threads, first look at what ctl is

	//可以看见ctl是一个AtomicInteger类型的对象,可以通过atomicInteger达到原子性的加减
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //传入两个int值进行做或运算,转化为二进制后,只有全部为0才为0,不然就为1.
    //对应上述的ctlOf(RUNNING, 0),我们提出一个猜想:更像是对RUNNING的状态值和初始化线程数0进行一个整合
    //我们先看看RUNNING代表什么再说。
    private static int ctlOf(int rs, int wc) {
    
     return rs | wc; }

    // runState is stored in the high-order bits
    //线程池的运行时状态存储在高位的比特中。 可以看见是-1向左进COUNT_BITS位
    private static final int RUNNING    = -1 << COUNT_BITS;
    //可以看见COUNT_BITS是Integer的size -3 = 29.
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //通过到这里可以知道RUNNING的值是 11100000 00000000 00000000 00000000
    //根据官方的解释,最高的三位可以代表当前线程池的运行状态,其他位数则可以代表当前线程池中的线程数量
    //再来看看其他的线程池状态,仅看最高位即可
    //shutdown的运行状态则为 00000000 00000000 00000000 00000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //stop 的运行状态则为 00100000 00000000 00000000 00000000
    private static final int STOP       =  1 << COUNT_BITS;
    //tidying 的运行状态为 01000000 00000000 00000000 00000000
    private static final int TIDYING    =  2 << COUNT_BITS;
    //termination 的运行状态为 01100000 00000000 00000000 00000000
    private static final int TERMINATED =  3 << COUNT_BITS;
	//可以看见除了RUNNNING,其他的状态都是>=0的,这在后面会有用
	
	//介绍到这里,我们再来认识几个参数
	//这是获取线程池的活跃线程的最大容量。 相当于是 stop的值 - 1 
	// 00100000 00000000 00000000 00000000 - 1 = 00011111 1111111 11111111 11111111
	private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
	//所以以下两个方法,就可以获取到对应ctl对象的线程池状态与当前线程池活跃线程数
	private static int runStateOf(int c)     {
    
     return c & ~CAPACITY; }
    private static int workerCountOf(int c)  {
    
     return c & CAPACITY; }

So we can summarize, ctl is an AtomicInteger object, ctl.get() can get the current thread pool status and the value of the number of threads. runStateOf(int c) and workerCountOf(int c) can get the current thread pool status and number of active threads.
The first three bits of ctl are the status value of the thread pool and the number of active threads in the thread pool composed of other bits.

to sum up

A very basic introduction to the structure of the thread pool and the setting of basic parameters.
In general, if we need to use a thread pool, we can directly use Executors to create and use it in normal use. If it is in development, it is recommended to manually create ThreadPoolExecutor.
The remaining content, such as the execution flow of the thread pool, is too much...I will write it together in the next article.

Guess you like

Origin blog.csdn.net/qq_41762594/article/details/108370686