实现优先使用运行线程及调整线程数大小的线程池

当前在JDK中默认使用的线程池 ThreadPoolExecutor,在具体使用场景中,有以下几个缺点

  1. core线程一般不会timeOut
  2. 新任务提交时,如果工作线程数小于 coreSize,会自动先创建线程,即使当前工作线程已经空闲,这样会造成空闲线程浪费
  3. 设置的maxSize参数只有在队列满之后,才会生效,而默认情况下容器队列会很大(比如1000)

如一个coreSize为10,maxSize为100,队列长度为1000的线程池,在运行一段时间之后的效果会是以下2个效果:

  1. 系统空闲时,线程池中始终保持10个线程不变,有一部分线程在执行任务,另一部分线程一直wait中(即使设置allowCoreThreadTimeOut)
  2. 系统繁忙时,线程池中线程仍然为10个,但队列中有还没有执行的任务(不超过1000),存在任务堆积现象

本文将描述一下简单版本的线程池,参考于 Tomcat ThreadPoolExecutor, 实现以下3个目标

  1. 新任务提交时,如果有空闲线程,直接让空闲线程执行任务,而非创建新线程
  2. 如果coreSize满了,并且线程数没有超过maxSize,则优先创建线程,而不是放入队列
  3. 其它规则与ThreadPoolExecutor一致,如 timeOut机制

首先看一下ThreadPoolExecutor的执行逻辑, 其基本逻辑如下

  1. 如果线程数小于coreSize,直接创建新线程并执行(coreSize逻辑)
  2. 尝试放入队列
  3. 放入队列失败,则尝试创建新线程(maxSize逻辑)

而执行线程的任务执行逻辑,就是不断地从队列里面获取任务并执行,换言之,即如果有执行线程,直接往队列里面放任务,执行线程就会被通知到并直接执行任务.

空闲线程优先

空闲线程优先在基本逻辑中,即如果线程数小于coreSize,但如果有空闲线程,就取消创建线程的逻辑. 在有空闲线程的情况下,直接将任务放入队列中,即达到任务执行的目的。

这里的逻辑即是直接调整默认的ThreadPoolExecutor逻辑,通过重载 execute(Runnable) 方法达到效果. 具体代码如下所示:

public void execute(Runnable command) {
    //此处优先处理有活跃线程的情况,避免在<coreSize时,直接创建线程
    if(getActiveCount() < getPoolSize()) {
        if(pool1.offer(command)) {
            return;
        }
    }

    super.execute(command);
}

coreSize满了优先创建线程

从之前的逻辑来看,如果放入队列失败,则尝试创建新线程。在这个时候,相应的coreSize肯定已经满了。那么,只需要处理一下逻辑,将其offer调整为false,即可以实现相应的目的。

这里的逻辑,即是重新定义一个BlockingDeque,重载相应的offer方法,相应的参考如下:

public boolean offer(Runnable o) {
    //这里的parent为ThreadPoolExecutor的引用
    int poolSize = parent.getPoolSize();
    int maxPoolSize = parent.getMaximumPoolSize();

    //还没到最大值,先创建线程
    if(poolSize < maxPoolSize) {
        return false;
    }

    //默认逻辑
    return super.offer(o);
}

即判定当前线程池中线程数如果小于最大线程数,即直接返回false,达到放入队列失败的效果。

总结

按照以上的调整,只需要通过继承自默认的ThreadPoolExecutor和默认的BlockingQueue(如LinkedBlockingDeque),重载2个主要的方法 ThreadPoolExecutor#execute 和 LinkedBlockingDeque#offer 即达到调整的目的。

缺点在于,实现后的类,在定义时,需要互相引用,因为相应的逻辑中需要互相调用相应的方法,以处理逻辑。此外,ThreadPoolExecutor的相应方法 getXXX 方法,在调用时都为通过加锁式实现,以精确返回数据,这里在多线程环境中可能会存在一些性能上的考虑。

在Tomcat默认的worker线程池中,即是采用以上的逻辑来达到工作线程的调整逻辑。因此在spring boot tomcat embedded中,通过参数 server.tomcat.max-thread 和 min-thread 即是通过优先调整线程来达到控制工作线程的目的。 相应的处理类为 org.apache.tomcat.util.threads 下的 ThreadPoolExecutor 和 TaskQueue。

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/%e5%ae%9e%e7%8e%b0%e4%bc%98%e5%85%88%e4%bd%bf%e7%94%a8%e8%bf%90%e8%a1%8c%e7%ba%bf%e7%a8%8b%e5%8f%8a%e8%b0%83%e6%95%b4%e7%ba%bf%e7%a8%8b%e6%95%b0%e5%a4%a7%e5%b0%8f%e7%9a%84%e7%ba%bf%e7%a8%8b%e6%b1%a0.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注