一个例子了解ThreadPoolExecutor的参数含义及执行流程

ThreadPoolExecutor具有多个参数,为了能够用好ThreadPoolExecutor,我们需要了解参数的含义以及了解参数背后线程池执行的流程。如果只靠死记硬背,这么多参数很容易遗忘,因此本文设计了一个例子帮你了解任务提交到线程之后是怎样的执行流程,观察例子的运行结果再结合参数可以更直观的理解ThreadPoolExecutor的用法。

ThreadPoolExecutor的构造函数及参数解释

先看ThreadPoolExecutor的构造函数:

 public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize : 核心线程数
maximumPoolSize:最大线程数
keepAliveTime:线程空闲时间
TimeUnit: 时间单位
workQueue:任务队列
threadFactory:线程工厂
handler:拒绝策略

例子

这里我们设计一个例子,设置较少的线程,提交较多的任务,同时设置较小的队列大小,及较小的空闲时间,并让线程随机睡眠一段时间模拟程序执行时间。
这样就可以看到核心线程创建的过程,队列满的过程,最大线程数创建的过程,队列满了任务拒绝的过程,任务结束之后空闲线程销毁的过程。
先仔细看一下下面的代码,根据你的理解在脑海中想象一下运行结果会是怎样。

public class ThreadPoolTest {

    public static void main(String[] args) {

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                4,
                10,
                2,
                TimeUnit.MINUTES,
                new LinkedBlockingQueue<>(20),
                new ThreadPoolExecutor.DiscardPolicy());

        System.out.println("---- before commit");
        System.out.println("activeCount:"+executor.getActiveCount());
        System.out.println("queueSize:"+executor.getQueue().size());

        System.out.println("---- commit ... ");

        for (int i=0;i<60;i++){
            System.out.println();
            System.out.println("activeCount:"+executor.getActiveCount());
            System.out.println("queueSize:"+executor.getQueue().size());
            executor.execute(()->{
                int random = new Random().nextInt(5);
                int time = 500 * random;
                try{
                    Thread.sleep(time);
                }catch (Exception e){

                }
            });
        }

        System.out.println("----- committed");
        while(executor.getActiveCount()>0){
            System.out.println();
            System.out.println("activeCount:"+executor.getActiveCount());
            System.out.println("queueSize:"+executor.getQueue().size());
            try{
                Thread.sleep(800);
            }catch (Exception e){

            }
        }
        System.out.println("---- activeCount is 0 ");
        System.out.println("activeCount:"+executor.getActiveCount());
        System.out.println("queueSize:"+executor.getQueue().size());

        executor.shutdown();
    }
}

例子结果

---- before commit
activeCount:0
queueSize:0
---- commit ... 

activeCount:0
queueSize:0

activeCount:1
queueSize:0

activeCount:2
queueSize:0

activeCount:3
queueSize:0

activeCount:4
queueSize:0

activeCount:4
queueSize:1

activeCount:4
queueSize:2

activeCount:4
queueSize:3

activeCount:4
queueSize:4

activeCount:4
queueSize:5

activeCount:4
queueSize:6

activeCount:4
queueSize:7

activeCount:4
queueSize:8

activeCount:4
queueSize:9

activeCount:4
queueSize:10

activeCount:4
queueSize:11

activeCount:4
queueSize:12

activeCount:4
queueSize:13

activeCount:4
queueSize:14

activeCount:4
queueSize:15

activeCount:4
queueSize:16

activeCount:4
queueSize:17

activeCount:4
queueSize:18

activeCount:4
queueSize:19

activeCount:4
queueSize:20

activeCount:5
queueSize:20

activeCount:6
queueSize:20

activeCount:7
queueSize:20

activeCount:8
queueSize:20

activeCount:9
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20

activeCount:10
queueSize:20
----- committed

activeCount:10
queueSize:20

activeCount:10
queueSize:16

activeCount:10
queueSize:9

activeCount:10
queueSize:4

activeCount:6
queueSize:0
---- activeCount is 0 
activeCount:0
queueSize:0

Process finished with exit code 0

上面贴出了程序运行的完整日志,结合程序代码,一行行行的看下日志,便能还原出程序执行的过程,进而理解线程池运作的流程。

总结

观察日志,执行过程分为以下几个阶段:

  1. 初始阶段:活跃线程为0 ,队列为0,即参数中的corePoolSize配置的核心线程还未创建。
    2.提交任务时核心线程开始创建并处理任务,此阶段随着任务的提交核心线程有1个到达了总核心线程数4(我们的corePoolSize=4)。
    3.随着任务的提交,我们配置的4个核心线程创建完毕,新的任务新任务入队列(此时并没有创建新的线程)。
    4.接下来任务还在提交,于是队列满了,核心线程之外的线程开始创建,并逐步达到最大线程数10(我们的maximumPoolSize=10)。
    5.最大线程数已达到,且队列满了,新提交的任务开始被拒绝了。因为这里使用的是DiscardPolicy,所以什么都不做,你可以自定义拒绝策略观察效果。
    6.已提交的任务开始慢慢被执行完毕了,队列大小在逐步减小。
    7.队列空了,线程开始空闲了。
    8.空闲的线程达到最大空闲时间逐个停掉了,直至活跃线程数为0。
    9.程序结束。
posted @ 2021-02-19 23:36  逃离沙漠  阅读(689)  评论(0编辑  收藏  举报