Tomcat深入浅出

1、Tomcat的线程池

1.1、基本介绍

Tomcat 线程池是 Tomcat 服务器用于管理和分配线程的一种机制。在处理客户端请求时,Tomcat 会从线程池中获取线程来执行任务,而不是为每个请求都创建一个新的线程。这样可以有效减少线程创建和销毁的开销,提高服务器的性能和资源利用率。

 

1.2、主要组件

1.2.1、核心线程数(corePoolSize)

这是线程池中的基本线程数量。当有新的请求到来,并且当前线程数量小于核心线程数时,线程池会创建新的线程来处理请求。例如,如果核心线程数设置为 10,那么在最初的 10 个请求到来时,线程池会创建 10 个线程来分别处理这些请求。

在 Tomcat 的默认配置中,核心线程数是 10。

 

1.2.2、最大线程数(maximumPoolSize)

线程池允许的最大线程数量。当请求数量超过核心线程数,并且队列已满时,线程池会继续创建线程,直到线程数量达到最大线程数。例如,最大线程数设置为 50,在高并发情况下,线程数量可能会从核心线程数逐渐增加到最大线程数来应对大量请求。

Tomcat 线程池默认的最大线程数是200

 

1.2.3、队列(BlockingQueue)

队列主要用于存放等待线程池中的线程来处理的客户端请求。当线程池中的所有线程都在忙碌,且尚未达到最大线程数时,新到达的请求会先被放入队列中等待处理,这可以有效地平滑请求流量的高峰,避免因短时间内大量请求涌入而导致系统崩溃。一旦队列满了,并且线程池中的线程数量还未达到最大线程数,Tomcat 就会迅速创建新的线程来处理请求。或者如果线程池中的某个线程被释放了,该线程会从队列中获取等待处理的请求并进行处理。当线程数大于等于核心线程数时,正常情况下只有当队列已满时,才会创建新的线程来处理新请求,直到线程数量达到maximumPoolSize。 

  • 队列大小的影响
    • 较小队列:如果队列大小设置得较小,当并发请求量突然增大时,队列很快就会满。一旦队列满了,并且线程池中的线程数量还未达到最大线程数,Tomcat 就会迅速创建新的线程来处理请求。这可能会导致线程数量在短时间内快速增长,过多的线程可能会消耗大量的系统资源(如 CPU 和内存),甚至可能导致系统性能下降,出现资源竞争和上下文切换过于频繁等问题。
    • 较大队列:设置较大的队列可以容纳更多的等待请求,在一定程度上可以缓冲请求流量的高峰。但是,如果队列过大,可能会导致请求在队列中等待的时间过长,尤其是在高并发且处理速度较慢的情况下。长时间的等待可能会使客户端超时,影响用户体验。而且,过大的队列也会占用较多的内存资源。
  • 如何正确配置队列大小
    • 根据应用场景确定:对于高并发、请求处理速度快的应用(如一些高性能的 Web 服务),可以适当增大队列大小,以应对突发的流量高峰,同时合理设置最大线程数,避免线程数量过度增长。例如,在一个预计并发请求量可达 1000,平均请求处理时间较短(如 1 - 2 秒)的应用中,可以将队列大小设置为 500,最大线程数设置为 300。
    • 结合性能测试调整:通过性能测试工具(如 JMeter 等)对应用进行压力测试,观察不同队列大小设置下系统的性能表现,包括响应时间、吞吐量、资源利用率等指标。根据测试结果,优化队列大小的配置。例如,如果发现随着队列大小的增加,响应时间逐渐变长,但吞吐量变化不大,可能需要适当减小队列大小。

Tomcat 使用的默认请求队列是org.apache.tomcat.util.threads.TaskQueue,这个队列的大小在默认情况下没有严格的限制,不过在实际应用中,会受到 JVM 内存大小等因素的限制。例如,假设 JVM 中除了请求队列外,还有其他组件占用了一定的内存,剩余内存只够再容纳 100 个请求对象大小的空间。当队列中的请求数量已经达到这个极限时,并且再加入新请求就可能导致内存溢出,这种情况下队列实际上已满。

 

1.2.4、线程存活时间(keepAliveTime)

当线程池中的线程数量超过核心线程数,并且在一段时间内没有任务需要处理时,这些多余的线程会在存活时间过后被销毁。例如,如果存活时间设置为 60 秒,那么当一个线程空闲了 60 秒后,且线程数量大于核心线程数,这个线程就会被销毁。

Tomcat 线程池默认的线程存活时间是60秒。

 

1.3、线程工作流程

  1. 请求到达:当客户端请求到达 Tomcat 服务器时,首先会检查线程池中的线程状态。
  2. 分配线程:如果有空闲线程(线程数量小于等于核心线程数),则将请求分配给空闲线程进行处理;如果没有空闲线程,但线程数量小于最大线程数,并且队列已满,则创建新的线程来处理请求;如果线程数量达到最大线程数,并且队列已满,那么请求可能会被拒绝(根据拒绝策略)。
  3. 线程回收:在处理完请求后,线程会回到线程池等待下一个任务。如果线程空闲时间超过存活时间,并且线程数量超过核心线程数,那么该线程会被回收。

 

1.4、线程大小的影响

线程大小的影响对CPU的影响:

  • 太大:当最大线程数较大时,在高并发场景下会有更多的线程同时运行。这可能会导致 CPU 使用率大幅上升,因为每个线程都需要占用一定的 CPU 时间片来执行任务。例如,在一个复杂的 Web 应用中,如果最大线程数设置为 500,并且同时有大量请求到来,500 个线程可能会频繁地争夺 CPU 资源,使得 CPU 处于高负载状态。如果 CPU 核心数较少,可能会出现线程频繁切换的情况,这会消耗额外的 CPU 资源用于上下文切换,从而降低系统的整体性能。
  • 太小:反之,若最大线程数设置得较小,如只有 10,在高并发情况下可能无法充分利用 CPU 资源。因为 CPU 可能有足够的能力处理更多的线程,但由于线程数的限制,请求只能在队列中等待或者被拒绝,导致系统的吞吐量较低。
线程大小的影响对内存的影响:
  • 当最大线程数过大时,可能会导致内存资源紧张,甚至出现内存溢出的情况。每个线程都有自己的栈空间,用于存储局部变量、方法调用信息等。线程数量越多,占用的内存空间就越大。例如,在 JVM 中,默认情况下每个线程的栈大小可能是 1MB 左右(这个大小可以通过参数调整)。如果最大线程数设置为 1000,仅线程栈就可能占用 1GB 以上的内存空间。此外,线程的创建和管理也需要一定的内存开销,包括线程对象本身的存储、线程调度相关的数据结构等。所以当最大线程数过大时,可能会导致内存资源紧张,甚至出现内存溢出的情况。

 

1.5、配置与优化

  • 根据应用场景配置参数:
    • 对于高并发的 Web 应用,如电商网站的促销活动期间,可以适当增大核心线程数和最大线程数,同时选择合适的队列类型和大小,以应对大量的请求。而对于低并发的应用,如企业内部的管理系统,可以设置较小的核心线程数和最大线程数,以避免资源浪费。
    • 例如,在一个预估并发请求数在 100 - 200 之间的应用中,可以将核心线程数设置为 50,最大线程数设置为 200,队列大小设置为 100,这样在一般情况下,50 个核心线程可以处理大部分请求,当请求增多时,最多可以利用 200 个线程和 100 个队列位置来处理高峰流量。
  • 监控与调整:
    • 可以使用 Tomcat 自带的监控工具或者第三方监控工具(如 JMX)来监控线程池的使用情况,包括线程的数量、队列的长度、线程的状态等。根据监控结果,及时调整线程池的参数,以优化服务器的性能。例如,如果发现请求经常被拒绝,可能需要增大最大线程数或队列大小。

 

1.6、Tomcat 最大线程数和Java程序创建的线程的关系

事实上,Tomcat 服务器线程池和 Java 程序通过 thread 创建的线程可以理解为互相独立的。Tomcat 的最大线程数并不影响 Java 程序创建的线程,比如当 Tomcat 服务器线程池最大线程数设为 10 时,通过 Java 创建的线程也可以超过 10。

 

两者互相独立:

  • Web 服务器线程池:主要用于处理客户端的 HTTP 请求。当有客户端向 Web 服务器发送请求时,服务器会从线程池中取出一个空闲线程来处理该请求,包括解析请求头、调用相应的业务逻辑、生成响应内容等操作。例如,用户在浏览器中访问一个网页,Web 服务器线程池中的线程会负责响应用户的请求并返回页面内容。
  • Java 程序通过 Thread 创建的线程:通常用于执行一些独立于 Web 请求处理的特定任务。这些任务可以是耗时的计算、后台数据处理、定时任务等。比如,在一个 Java 程序中需要进行大量的数据加密计算,就可以创建一个新线程来执行这个加密任务,避免阻塞主线程。
资源分配不同:
  • Web 服务器线程池:其最大线程数限制了同时处理请求的最大并发数,Web 服务器会根据这个限制来分配系统资源,确保系统不会因为过多的并发请求而耗尽资源。例如,如果将 Web 服务器线程池的最大线程数设置为 100,那么服务器最多同时有 100 个线程在处理请求。
  • Java 程序通过 Thread 创建的线程:创建的线程数量没有固定的上限(受限于系统资源或者操作系统的配置限制),开发者可以根据业务需求动态创建线程。但如果创建的线程数量过多,可能会导致系统资源耗尽,影响系统性能。
    • (操作系统限制线程数说明:例如,在 Linux 系统中,可以通过修改 /etc/security/limits.conf 文件来调整用户级别的线程数量限制。不同的 Linux 发行版和配置可能会有不同的默认值,一般在几千个左右。如果超过这个限制,Java 程序在尝试创建新线程时会抛出 OutOfMemoryError: unable to create new native thread 异常。)

 

两者之间的联系:

  • 它们都运行在同一个 JVM 进程中,共享系统的 CPU、内存等资源。如果 Java 程序通过 Thread 创建了大量的线程,占用了过多的系统资源,可能会导致 Web 服务器线程池的线程获取资源不足,从而影响 Web 请求的处理性能。例如,当大量的自定义线程进行密集的 CPU 计算时,Web 服务器线程可能会因为 CPU 资源不足而处理请求变慢。

 

posted @ 2025-01-03 15:48  wenxuehai  阅读(273)  评论(0)    收藏  举报
//右下角添加目录