ThreadPoolExecutor 是如何工作的?那么多参数到底怎么用?

ThreadPoolExecutor的参数含义

如果你在工作中用到线程池了,那一定要用 ThreadPoolExecutor 这个类,这个类里面有常用5个参数(实际上是7个),但是呢,有几种情况,使用的时候往往分不清,之后觉得线程池很难,今天我就用例子来说明一下具体用法,今后不再犯难ThreadPoolExecutor。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        ...
    }

corePoolSize:核心线程数,包括空闲线程

maximumPoolSize:线程池里允许存在的最大的线程数

keepAliveTime:设置非核心线程的空闲线程的存活时间。当线程数量>核心线程数,并且不大于最大线程数,并且队列用的是 SynchronousQueue对列的时候,这个参数就起作用了:在线程执行完任务后的指定时间内不将这个空闲线程删除,超过时间后,则将这个空闲线程删除。注意删除的空闲线程是非核心线程。

unit:是 keepAliveTime的时间单位,比如秒,分钟之类的

BlockingQueue:存放未执行的任务队列

ThreadPoolExecutor是如何工作的

定义公共的任务

Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + "在运行" + System.currentTimeMillis());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

当前线程数<=corePoolSize,马上使用核心线程执行任务,无论用 LinkedBlockingDeque还是
SynchronousQueue 队列都不会放入工作队列里(因为没有那么多任务,这些任务都不够核心线程处理的),由于都是核心线程在处理,所以
keepAliveTime 设置0L或者其他时间都不会生效的。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 8, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        for (int i = 0; i < 5; i++) {
            threadPoolExecutor.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor.getPoolSize());
        System.out.println("A===" + threadPoolExecutor.getQueue().size());
        Thread.sleep(3000);
        System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor.getPoolSize());
        System.out.println("B===" + threadPoolExecutor.getQueue().size());

结果
在这里插入图片描述
核心线程数无论是开始还是到最终都是5(核心线程数不会被删除,只是执行完任务后成为空闲状态),当前线程数也是5,队列数为0

对于 ThreadPoolExecutor 的下面方法可以这样理解:

threadPoolExecutor.getCorePoolSize():车的标准载客人数

threadPoolExecutor.getPoolSize():车中正在载客数

threadPoolExecutor.getMaximumPoolSize():车最大的载客人数(可以超载)

threadPoolExecutor.getQueue().size():车后面的拓展车厢载客人数

maximumPoolSize>=当前线程数>corePoolSize,并且使用的是LinkedBlockingDeque队列的话,那么马上使用核心线程执行任务,多余的任务放到这个队列里等待执行,每当核心线程里有一个线程执行完,那么就去队列拿一个继续执行,所以永远不会存在非核心线程,自然
keepAliveTime 设置0L或者其他时间都不会生效的。

ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(5, 8, 10L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        for (int i = 0; i < 7; i++) {
            threadPoolExecutor1.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor1.getPoolSize());
        System.out.println("A===" + threadPoolExecutor1.getQueue().size());
        Thread.sleep(3000);
        System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor1.getPoolSize());
        System.out.println("B===" + threadPoolExecutor1.getQueue().size());

结果
在这里插入图片描述
执行的时候,核心线程还是5个,正在工作的也是5个,还有2个放在队列里等待执行,一旦核心线程执行完任务就去队列里取任务继续执行,直到都完成

注意:此时为什么不启动非核心线程,而是要放在队列里呢?就是因为使用的是LinkedBlockingDeque队列,理论上这个队列默认情况下是Integer.MAX_VALUE这个长度的,队列不满,不足以去启动非核心线程。而SynchronousQueue队列不一样,它没有容量之说,随存随取,否则就堵塞,所以这个队列必须启动非核心线程。

maximumPoolSize>=当前线程数>corePoolSize,并且使用的是SynchronousQueue队列的话,那么马上使用核心线程执行任务,同时再启动非核心线程去执行多余的任务,此时这个
keepAliveTime就生效了,如果设置为0L则表示这个非核心线程执行完后就删除了,如果设置非0L,则表示在非核心线程执行完后制定时间后删除。

当设置0L,执行完就删除了

ThreadPoolExecutor threadPoolExecutor1=new ThreadPoolExecutor(5,8,0L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
        for (int i=0;i<7;i++){
            threadPoolExecutor1.execute(runnable);
        }
        
        Thread.sleep(500);
         System.out.println("A==="+threadPoolExecutor1.getCorePoolSize());
         System.out.println("A==="+threadPoolExecutor1.getPoolSize());
         System.out.println("A==="+threadPoolExecutor1.getQueue().size());
         System.out.println("休息2秒==="+System.currentTimeMillis());
         Thread.sleep(2000);
         System.out.println("B==="+threadPoolExecutor1.getCorePoolSize());
         System.out.println("B==="+threadPoolExecutor1.getPoolSize());
         System.out.println("B==="+threadPoolExecutor1.getQueue().size());
         System.out.println("结束了==="+System.currentTimeMillis());
        

结果
在这里插入图片描述
当设置3L,到期后才会删除

ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(5, 8, 3L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        for (int i = 0; i < 7; i++) {
            threadPoolExecutor1.execute(runnable);
        }


        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor1.getPoolSize());
        System.out.println("A===" + threadPoolExecutor1.getQueue().size());
        System.out.println("休息2秒===" + System.currentTimeMillis());
        Thread.sleep(2000);
        System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor1.getPoolSize());
        System.out.println("B===" + threadPoolExecutor1.getQueue().size());
        System.out.println("休息5秒===" + System.currentTimeMillis());
        Thread.sleep(5000);
        System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor1.getPoolSize());
        System.out.println("B===" + threadPoolExecutor1.getQueue().size());
        System.out.println("结束了===" + System.currentTimeMillis());

结果
在这里插入图片描述

当前线程数>maximumPoolSize,并且使用的是LinkedBlockingDeque队列的话,那么马上使用核心线程执行任务,多余的任务放到这个队列里等待执行,每当核心线程里有一个线程执行完,那么就去队列拿一个继续执行,所以永远不会存在非核心线程,即使当前线程数已经超过了最大的线程数,但是由于队列基本无限大,基本不可能满,此时最大线程数都没有什么意义了,真正工作的只有核心线程,其余的都去排队了,自然 keepAliveTime 设置0L或者其他时间都不会生效的。所以newFixdThreadExecutor和newSingleThreadExecutor才会创立核心线程数和最大线程一致吧,并且超时时间设置成0(其实和第二种情况是一样的,之所以再重复一遍是为了与下面这种情况做对比)

ThreadPoolExecutor threadPoolExecutor1 = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
        for (int i = 0; i < 5; i++) {
            threadPoolExecutor1.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor1.getPoolSize());
        System.out.println("A===" + threadPoolExecutor1.getQueue().size());
        Thread.sleep(2000);
        System.out.println("B===" + threadPoolExecutor1.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor1.getPoolSize());
        System.out.println("B===" + threadPoolExecutor1.getQueue().size());
        //不再添加新的任务了,但会把没执行完的继续执行完才关闭,不会阻塞主线程
        threadPoolExecutor1.shutdown();
        System.out.println("执行shutdown方法了,不会阻塞主线程");

结果
在这里插入图片描述

当前线程数>maximumPoolSize,并且使用的是SynchronousQueue队列的话,那么只会启动maximumPoolSize
个线程去执行任务(其中包含核心线程),对于超过最大线程数的线程,则抛出异常,不去处理

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 8, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor.getPoolSize());
        System.out.println("A===" + threadPoolExecutor.getQueue().size());
        Thread.sleep(3000);
        System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor.getPoolSize());
        System.out.println("B===" + threadPoolExecutor.getQueue().size());

结果
在这里插入图片描述

当LinkedBlockingDeque指定长度的时候(比如2),并且maximumPoolSize>=当前线程数>corePoolSize,首先启动核心线程数,然后把队列填满,剩下的(8-4-2)启动非核心线程

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2));
        for (int i = 0; i < 8; i++) {
            threadPoolExecutor.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor.getPoolSize());
        System.out.println("A===" + threadPoolExecutor.getQueue().size());
        Thread.sleep(3000);
        System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor.getPoolSize());
        System.out.println("B===" + threadPoolExecutor.getQueue().size());

结果
在这里插入图片描述

当LinkedBlockingDeque指定长度的时候,(当前任务数-核心线程数-队列长度)>(最大线程数-核心线程数【也就是最多能启动的非核心线程数】)时候,就会报错拒绝掉多余的任务,正在执行还有队列里等着的任务会继续执行

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2));
        for (int i = 0; i < 12; i++) {
            threadPoolExecutor.execute(runnable);
        }

        Thread.sleep(500);
        System.out.println("A===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("A===" + threadPoolExecutor.getPoolSize());
        System.out.println("A===" + threadPoolExecutor.getQueue().size());
        Thread.sleep(3000);
        System.out.println("B===" + threadPoolExecutor.getCorePoolSize());
        System.out.println("B===" + threadPoolExecutor.getPoolSize());
        System.out.println("B===" + threadPoolExecutor.getQueue().size());

结果
在这里插入图片描述
以上就是ThreadPoolExecutor的常见用法了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值