ThreadPoolExecutor的线程是如何增长的

在使用ThreadPoolExecutor时,我也跟参考文章作者(估计很多人也一样)产生了同样的误解:

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

看这个参数很容易让人以为是线程池里保持corePoolSize个线程,如果不够用,就加线程入池直至maximumPoolSize大小,如果还不够就往workQueue里加,如果workQueue也不够就用RejectedExecutionHandler来做拒绝处理。

研究了下其构造函数入参,对于其中 workQueue 和 workThread 是如何增长的有一些疑问:究竟是先创建工作线程处理新任务,还是先放到queue中等待再等workThread空了再处理。就此Google了下,并且探寻了下源码,通过源码发现了门道。

一篇参考文章的说法:

“3)当workQueue放不下新入的任务时,新建线程入池,并处理请求,如果池子大小撑到了maximumPoolSize就用RejectedExecutionHandler来做拒绝处理”

我来补充一下这其中的细节,逻辑解析自 jdk11 ThreadPoolExecutor.excecute():

  1. 线程池引入新任务时,先判断当前线程数是否 < corePoolSize;是则新建一个core线程专门处理这个新任务;否则下一步;
  2. 将新任务加入队列,如果能成功加入队列(队列没满)则等待处理;否则尝试新建work线程,如果work线程也加不了(高于maximumPoolSize),那就 reject 这个任务。

所以这就可以解答最初的问题:
(1)当任务少到不及 coreThreadSize 大小或者 coreThreadSize 就能处理得过来时,是用不到 workQueue 的;
(2)当 coreThread 满载时,新进来的任务会先进入 workQueue(并不是直接就创建workThread),加到 queue 后,二次检查:若当前没有 workThread 那就创建一个,否则就暂时完成处理,等待已有线程处理 queue。
(3)如果 workQueue 也满了加不了,那就创建一个 workThread 单独去处理这个 task,如果 workThread 也满了加不了,那就 reject 这个 task!
(4)综上,coreThreadSize 、queueSize、maxThreadSize 这三者要结合起来,根据实际情况权衡大小!如果任务很快就能处理完,并且新任务进来的速度还不如线程处理任务的速度,那大概率 coreThread 就能够应付自如,根本用不到 queue,queueSize 的大小无需配多大的;如果任务生产速度大于几个 coreThread 的消费速度,那必定是要入队的,至于 queueSize 设多大,就要权衡消费跟生产之间的效率差了。比如线程消费速率为 10 task/ min,而 task 生产速率为 60 task/min,1:5 关系,假定 coreThreadSize = 3,那么每分钟有 30 task无法处理,这 30 task 的缺口就需要 queue 或者 maxThreadSize - coreThreadSize 去弥补,比如我可以设定 maxThreadSize = coreThreadSize + 3,那我就多了 3 * 10 = 30 的消费力,正好弥补缺口,那么 queue 不需要设多大,线程也应付地过来(此时 queue 的作用更多是为了在 workThread 创建完毕之前暂存task),但是注意,如果任务是一次性生产完,再等待多线程陆续处理的,那么queueSize就不能低于总的任务数。假设 (maxThreadSize - coreThreadSize)* 10 的消费力仍有缺口,如果不想触发 reject 那 queue 就要考虑设长一些。当然以上计算只是理想状态下的计算,实际场景下,消费力并不是随着 thread 增加而线性增长,跟 CPU 核数和CPU时间片和任务切换等等都有关系。
(5)queueSize 并不是越大越好的,很简单,如果队列无限增长,内存会 OOM~
ThreadPoolExecutor.excecute() 源码:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

参考阅读:
ThreadPoolExecutor运转机制及BlockingQueue详解

ThreadPoolExecutor线程池参数设置技巧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值