首页 > 编程语言 >【tomcat】Tomcat如何扩展Java线程池原理

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

时间:2024-07-14 15:54:49浏览次数:21  
标签:Java tomcat Tomcat 队列 创建 任务 线程 执行 自定义

池化技术

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

Java线程池核心原理

在这里插入图片描述
不清楚线程池工作原理的,可以看如下文章,从使用到源码解析。

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

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

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

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

Tomcat自定义线程池

    // 自定义的线程队列 最大值是 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原生线程池抛出拒绝策略。尝试放入任务队列中,如果失败,抛出异常。

    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参数来限制任务队列的长度。

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(), 定制自己的任务处理流程。

标签:Java,tomcat,Tomcat,队列,创建,任务,线程,执行,自定义
From: https://blog.csdn.net/jia970426/article/details/140413304

相关文章

  • Java中Lambda表达式的应用
    Java中Lambda表达式的应用大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!Lambda表达式的基础语法Lambda表达式是Java8引入的一个重要特性,它提供了一种更简洁、更函数式的方式来编写匿名函数。Lambda表达式主要由参数列表、箭头符号(->)和方法体组成。例......
  • 深入解析Java中的泛型
    深入解析Java中的泛型大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!泛型的基本概念与语法在Java中,泛型(Generics)是一种强大的特性,它允许我们在编写代码时使用参数化类型。泛型提供了编译时类型安全检查,使得程序更加健壮和可维护。packagecn.juwatech.......
  • Java中的异常处理最佳实践
    Java中的异常处理最佳实践大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!异常处理的基本原则在Java编程中,异常处理是保证程序健壮性和可靠性的重要手段之一。良好的异常处理实践可以提升代码的可读性和维护性,下面我们来深入探讨Java中的异常处理最佳实......
  • Java中的设计模式详解
    Java中的设计模式详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!单例模式在实际开发中,经常会遇到需要保证一个类只有一个实例的情况。单例模式通过私有化构造方法和静态方法来确保只有一个实例被创建。以下是一个示例:packagecn.juwatech.designpa......
  • Java中的序列化与反序列化
    Java中的序列化与反序列化大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!序列化与反序列化概述在Java中,序列化是将对象转换为字节流的过程,可以将对象持久化保存到文件、数据库或网络传输中。反序列化则是将字节流恢复为对象的过程。Java中的序列化与反......
  • Java中的内存模型详解
    Java中的内存模型详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!Java内存模型概述Java内存模型(JavaMemoryModel,JMM)定义了Java程序中多线程并发访问共享变量的规范,确保多线程间的内存可见性、原子性和有序性。理解Java内存模型对于编写并发安全的......
  • Java中的Fork/Join框架详解
    Java中的Fork/Join框架详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java中,Fork/Join框架是一种用于并行处理任务的强大工具,特别适用于那些可以递归地分解成更小任务的场景。Fork/Join框架基于“工作窃取”算法,允许空闲的线程从那些繁忙的线程那......
  • Java中的CompletableFuture详解
    Java中的CompletableFuture详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代Java编程中,异步编程变得越来越重要。Java8引入了CompletableFuture,它极大地简化了异步编程的复杂性。CompletableFuture不仅支持异步操作,还提供了丰富的API来处理异步......
  • Java中的ReentrantLock详解
    Java中的ReentrantLock详解大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java编程中,多线程同步是一个常见的需求。为了保证多个线程对共享资源的安全访问,Java提供了多种锁机制,其中ReentrantLock是一个重要的工具。本文将详细介绍ReentrantLock的使用,......
  • 深入理解Java中的反射机制
    深入理解Java中的反射机制大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在Java开发中,反射机制是一种非常强大的工具,它允许程序在运行时检查和操作类、方法、字段等。反射机制不仅在框架开发中有着广泛的应用,而且在某些动态需求场景中也是必不可少的。本......