Talk about the java thread pool ThreadPoolExecutor (1)

Regarding the java thread pool, you may not write much by hand. If you use it, you usually use the packaged one directly. The specific internal implementation may be just a simple understanding. First, it is convenient to use the java thread pool. Of course, there is another more important one. Role, hey, everyone knows it.

In fact, understanding the operating mechanism of some source code in Java is very helpful to everyone's learning. On the one hand, the code in the source code is very streamlined, and the management between logic is very elegant. On the other hand, after familiarizing with the source code, it is in actual production. It’s easy to connect to a certain node that may have a problem, otherwise it will really pull out a lot of logs, and then check and retry a little bit, especially concurrency problems, it can really force people to crash.

Today, let’s talk about the thread pool. Let’s go from the shallower to the deeper, a little bit, let’s post a construction method and introduce a few key attributes. The key to the thread pool is ThreadPoolExecutor. Everyone try to remember this word. Otherwise, you talk about thread pool every day, but I don't know which type it is. I was really asked about it. It's too embarrassing. Look at the construction method

  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.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 construction method of the thread pool almost ultimately points to this construction method. This construction method is to limit several attributes, and then assign values, which is easy to understand. Let's look at each attribute separately.

  • corePoolSize: The number of core threads. When it comes to thread pool, everyone must have a concept of thread pool. This is a place where multiple threads are stored, just like a pool. When you need it, take out one thread for use. Therefore, there must be threads in this pool. Of course, if the threads are opened too many, it will definitely consume the performance of the server and take up a lot of resources. Therefore, when the thread pool is designed, a strategy for setting the thread pool is formulated. corePoolSize is a parameter of this strategy. Its meaning is the thread that the thread pool will always open, of course, it can also be 0. But it is best not to. Since the thread pool is opened, the business volume is not small, and the frequent creation and destruction of threads also consume resources.
  • maximumPoolSize: The maximum number of threads. Since it is a pool, the number of storage must be limited. Otherwise, if you pass over 10 million data, can you directly open 10 million threads? Don't be funny, the server will hang up early~ The maximum number of threads here is a limit. No matter how much data you send, once the threads reach the maximum number of threads, you are not allowed to continue to open threads. Of course, this parameter cannot be set to 0, otherwise what's the point of your thread pool? Of course maximumPoolSize>corePoolSize is a must.
  • keepAliveTime thread survival time, for example, corePoolSize=2, maximumPoolSize=3, at this time there is no task to be executed in the thread pool, then the extra thread can be shut down according to keepAliveTime
  • The unit time unit, for example, keepAliveTime is 3, unit is TimeUnit.HOURS, which means it will be closed after 3 hours, and unit is TimeUnit.SECONDS, which means 3 seconds.
  • workQueue Cache queue, as I just said, it's best not to keep the thread open, so what if there are thousands of items that need to be executed? At this time, the parameters that need to be executed are thrown into the cache queue, and then when a thread is free, it will go to the cache queue to fetch data, which is much more friendly.
  • threadFactory thread factory
  • The handler rejects the policy. I'll talk about these two later.

 Well, the above are the most basic parameters of the thread pool, but to maintain an efficient operation of a thread pool, there is naturally a set of internal mechanisms. Let's start with the latest book and look at these three parameters: corePoolSize, maximumPoolSize, and workQueue. These three parameters are obviously related. For example, if there are more threads, the workQueue can obviously put less data. When will the data be put in the workQueue and when will the threads be opened? and so on.

Just upload the code, everyone can see it more clearly.

public class ThreadPoolTest {

    public static BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10);

    public static void main(String[] args) throws Exception {
        // 创建一个核心线程为2,最大线程为8,缓存队列为10的线程池
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 8, 0, TimeUnit.SECONDS, workQueue);
        // 拒绝策略先不说,i最大值写小点
        for (int i = 0; i < 18; i++) {
            poolExecutor.execute(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName());
                    //睡眠下,如果线程运行太快,直接空出来了,就没法试验了
                    Thread.sleep(200);
                }
            });
        }
         // 关闭下线程池,不能让它一直跑着哈
         poolExecutor.shutdown();
    }

We created a thread pool with a maximum thread of 8 and a cache queue of 10, and filled in 18 tasks at the same time. As you can see, a total of 8 threads were started, which can be said to be used by each thread.

线程名:pool-1-thread-1
线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-5
线程名:pool-1-thread-4
线程名:pool-1-thread-6
线程名:pool-1-thread-7
线程名:pool-1-thread-8
线程名:pool-1-thread-5
线程名:pool-1-thread-8
线程名:pool-1-thread-1
线程名:pool-1-thread-7
线程名:pool-1-thread-4
线程名:pool-1-thread-3
线程名:pool-1-thread-6
线程名:pool-1-thread-2
线程名:pool-1-thread-7
线程名:pool-1-thread-1

But what if we change the task to 17?

for (int i = 0; i < 17; i++) {
            poolExecutor.execute(new Runnable() {
                @SneakyThrows
                @Override
                public void run() {
                    System.out.println("线程名:" + Thread.currentThread().getName());
                    //睡眠下,如果线程运行太快,直接空出来了,就没法试验了
                    Thread.sleep(200);
                }
            });
        }

You will find that only 7 threads are started

线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-6
线程名:pool-1-thread-7
线程名:pool-1-thread-1
线程名:pool-1-thread-5
线程名:pool-1-thread-4
线程名:pool-1-thread-3
线程名:pool-1-thread-2
线程名:pool-1-thread-4
线程名:pool-1-thread-5
线程名:pool-1-thread-1
线程名:pool-1-thread-7
线程名:pool-1-thread-6
线程名:pool-1-thread-2
线程名:pool-1-thread-3
线程名:pool-1-thread-7

The core thread must have been started, and then 17 tasks came, 7 threads were opened, and 10 cache queues. Did the partners find anything? Originally, 8 threads could be created, and then 9 data was placed in the cache queue, but now only 7 threads are created, and 10 data is placed in the cache queue. Obviously, the running or use order of the thread pool is first coreSize> workQueue> maximumPoolSize. Only when the core thread has been occupied and the workQueue is full, the threads will be opened again to process data until the maximum number of threads is opened.

What if there are too many tasks? For example, there are 100 tasks. Of course, an exception is thrown. The default rejection strategy is to do this. Of course, you can also define your own rejection strategy. I'll talk about it later.

That's it for today, it's a bit simple, everyone just glance at it, don't laugh.

No sacrifice,no victory~

Guess you like

Origin blog.csdn.net/zsah2011/article/details/110187618