【tomcat】Tomcat如何扩展Java线程池原理

池化技术

在后端中,对于经常使用池化就是来提升系统性能,比如数据库连接池、线程池连接池等,本质都是利用空间换时间的来提升性能,用来避免资源的频繁创建和销毁,以此提高资源的复用率,所以合理设置系统所需的线程池大小非常重要,一般都需要结合线程启动监控系统来观察,查看设置的是否合理。但是也有缺点,那就是如何无脑的设置,可能会占用过多的内存。所以要避免出现空间过度使用出现内存泄露和频繁垃圾回收的问题。

Java线程池核心原理

不清楚线程池工作原理的,可以看如下文章,从使用到源码解析。

【源码解析】聊聊线程池 实现原理与源码深度解析(一)

【源码解析】聊聊线程池 实现原理与源码深度解析(二)

【Java并发】聊聊线程池原理以及实际应用

【Java并发】聊聊创建线程池的几种方式以及实际生产如何应用

Tomcat自定义线程池

java 复制代码
    // 自定义的线程队列 最大值是 Integer.MAX_VALUE;
   taskqueue = new TaskQueue(maxQueueSize);
   // 自定义线程工厂,名称是tomcat-exec- (所以这就是为什么日志中是打印的它)
   TaskThreadFactory tf = new TaskThreadFactory(namePrefix,daemon,getThreadPriority());
   // 自定义线程池
   // 最小线程数 20 最大线程数 200  空闲时间 60S
   executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), maxIdleTime, TimeUnit.MILLISECONDS,taskqueue, tf);
    

可以看到 上面在初始化线程池的时候,创建了一个自定义线程队列以及一个线程工厂。

tomcat自定义线程处理流程

这里对着执行流程进行梳理下,

1.前corePoolSize个任务时,就创建核心线程处理

2.再来任务,就放入任务队列中让所有线程去抢,队列满了,创建临时线程执行。

3.达到最大线程maxNumPoolSize, 继续尝试把任务添加到任务队列中。

4.缓冲队列也满了,插入失败,执行拒绝策略。

我们看具体的code实现,可以发现,先调用java原生线程池执行,超过maximumPoolSize,java原生线程池抛出拒绝策略。尝试放入任务队列中,如果失败,抛出异常。

java 复制代码
    public void execute(Runnable command, long timeout, TimeUnit unit) {
        // 执行一个任务加1
        submittedCount.incrementAndGet();
        try {
            // 调用java原生线程池的execute去执行任务
            super.execute(command);
        } catch (RejectedExecutionException rx) {
            // 如果线程数达到最大 maximumPoolSize  Java原生线程池执行拒绝策略
            if (super.getQueue() instanceof TaskQueue) {
                final TaskQueue queue = (TaskQueue)super.getQueue();
                try {
                    // //继续尝试把任务放到任务队列中去
                    if (!queue.force(command, timeout, unit)) {
                        // 执行失败 -1
                        submittedCount.decrementAndGet();
                        // //如果缓冲队列也满了,插入失败,执行拒绝策略。
                        throw new RejectedExecutionException(sm.getString("threadPoolExecutor.queueFull"));
                    }
                } catch (InterruptedException x) {
                    //  // 执行失败 -1
                    submittedCount.decrementAndGet();
                    throw new RejectedExecutionException(x);
                }
            } else {
                //  // 执行失败 -1
                submittedCount.decrementAndGet();
                throw rx;
            }

        }
    }

tomcat自定义任务队列

从如下代码中可以看到,自定义了一个任务队列,因为这个队列是一个无界队列,达到核心线程后,就无法创建线程,直接将任务阻塞到队列中。所以通过 submittedCount.incrementAndGet(); submittedCount.decrementAndGet(); 记录当前已经提交到线程池,但是还没有执行完的任务个数。

在任务队列的长度无限制的情况下,让线程池有机会创建新的线程。
当然默认情况下Tomcat的任务队列是没有限制的,你可以通过设置maxQueueSize参数来限制任务队列的长度。

java 复制代码
public class TaskQueue extends LinkedBlockingQueue<Runnable> {
    // 构造方法 调用父类构造方法
    public TaskQueue(int capacity) {
        super(capacity);
    }

	  @Override
    // 线程池调用任务队列的方法时,当前线程数肯定已经大于核心线程数了
    public boolean offer(Runnable o) {
      //we can't do any checks
        if (parent==null) return super.offer(o);
        //we are maxed out on threads, simply queue the object
        // //如果线程数已经到了最大值,不能创建新线程了,只能把任务添加到任务队列。
        if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
        //we have idle threads, just add it to the queue
        //执行到这里,表明当前线程数大于核心线程数,并且小于最大线程数。
        //表明是可以创建新线程的,那到底要不要创建呢?分两种情况:

        //1. 如果已提交的任务数小于当前线程数,表示还有空闲线程,无需创建新线程
        if (parent.getSubmittedCount()<=(parent.getPoolSize())) return super.offer(o);
        //if we have less threads than maximum force creation of a new thread

        //2. 如果已提交的任务数大于当前线程数,线程不够用了,返回false去创建新线程
        if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
        //if we reached here, we need to add it to the queue

        //默认情况下总是把任务添加到任务队列
        return super.offer(o);
    }
}

小总结

虽然面试中,对于线程池的问题很多,但是如果我们可以结合tomcat自定义线程池的原理来进行复补充,那么不仅可以体现我们对框架内部理解的深度,也可以提升对八股文的应用能力。

tomcat是如何设计的,其实主要就是继承原生ThreadPoolExecutor,重写execute(), 定制自己的任务处理流程。

相关推荐
m0_748247806 分钟前
SOA(面向服务架构)全面解析
java·微服务·架构
码叔义8 分钟前
Jsonpath 使用说明
android·开发语言·javascript
m0_740154679 分钟前
Tomcat
java·tomcat
爱敲代码的小冰17 分钟前
【Excel】 Power Query抓取多页数据导入到Excel
android·java·excel
行十万里人生22 分钟前
Qt 对象树详解:从原理到运用
开发语言·数据库·qt·华为od·华为·华为云·harmonyos
原来是猿28 分钟前
蓝桥备赛(四)- 数组(下)
开发语言·数据结构·c++·算法
心流时间31 分钟前
[Java基础] JVM常量池介绍(BeanUtils.copyProperties(source, target)中的属性值引用的是同一个对象吗)
java·开发语言·jvm
网络安全Ash33 分钟前
Python网络安全脚本
开发语言·python·web安全
瑞金彭于晏1 小时前
通俗易懂版 Maven 科普,maven是什么?
java·maven
好看资源平台1 小时前
Java Web开发实战与项目——Spring Boot与Spring Cloud微服务项目实战
java