学习分享-Tomcat 的线程池在工作方式上与普通的 Java 线程池的区别

前言

最近在学习过程中遇到在某个场景下:修改某条数据时,给该线程上分布式写锁,然后引入延迟队列处理其他请求;这个方案有一定的缺点,因为在用到消息队列时,不存在占用过多线程从而导致OOM的问题,消费者组只会安排固定的几个线程去拉取消息,如果碰到上面那种拿不到锁的情况,阻塞等待就好。
上面这个场景设计到了Tomcat 的线程池,因此做一些分享。
Tomcat 的线程池在工作方式上有一些不同于普通的 Java 线程池 (如 ThreadPoolExecutor) 的地方,尤其是在处理线程的创建和任务的排队方面。

普通线程池(ThreadPoolExecutor)的工作方式

普通的 Java 线程池 (ThreadPoolExecutor) 按以下顺序处理任务:

  1. 核心线程:首先会创建和使用核心线程来处理任务,直到核心线程数达到 corePoolSize
  2. 阻塞队列:如果核心线程都在忙,并且有新的任务进来,任务会被放入阻塞队列(workQueue)中。
  3. 最大线程数:如果阻塞队列满了,并且还有新的任务,线程池会创建新的线程,直到线程总数达到 maximumPoolSize
  4. 拒绝策略:如果线程总数已经达到了 maximumPoolSize,并且阻塞队列也满了,新来的任务会根据拒绝策略(RejectedExecutionHandler)来处理。

Tomcat 线程池的工作方式

Tomcat 使用了一个自定义的线程池实现,它在处理请求时的行为与普通线程池有一些不同:

  1. 核心线程与最大线程:Tomcat 线程池中的 maxThreads 参数对应于普通线程池中的 maximumPoolSize,而 minSpareThreads 参数则与 corePoolSize 类似,但有一些不同。
  2. 直接创建新线程:Tomcat 在线程创建方面更加激进,当有新的请求进来时,如果所有当前线程都在忙,它会立即尝试创建新的线程,直到达到 maxThreads 数量。
  3. 任务队列:Tomcat 也有一个任务队列(acceptCount),但它的作用是当所有 maxThreads 都在忙时,用于排队等待处理的请求数量。如果这个队列也满了,新的请求会被拒绝或连接被关闭。
  4. 线程回收:Tomcat 会回收空闲的线程,保持至少 minSpareThreads 数量的线程处于空闲状态以便应对新的请求。

具体的区别

  • 线程创建时机

    • 普通线程池:先使用核心线程,然后任务进入队列,再创建额外线程。
    • Tomcat 线程池:在核心线程忙时,会直接创建新的线程直到 maxThreads
  • 队列的使用

    • 普通线程池:核心线程忙时,任务排队,队列满时才创建新的线程。
    • Tomcat 线程池:在所有线程都忙时,任务才进入队列。
  • 参数配置

    • 普通线程池:corePoolSize, maximumPoolSize, keepAliveTime, workQueue
    • Tomcat 线程池:maxThreads, minSpareThreads, acceptCount 等。

示例

普通线程池(ThreadPoolExecutor)示例
ExecutorService executorService = new ThreadPoolExecutor(
    10, // corePoolSize
    20, // maximumPoolSize
    60L, // keepAliveTime
    TimeUnit.SECONDS, // unit
    new LinkedBlockingQueue<Runnable>(100) // workQueue
);
Tomcat 线程池配置示例(在server.xml中)
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           maxThreads="200"
           minSpareThreads="25"
           acceptCount="100"
           maxConnections="5000"/>

在这个配置中:

  • maxThreads:最大线程数,表示 Tomcat 能够创建的最大工作线程数(类似于 maximumPoolSize)。
  • minSpareThreads:最小空闲线程数,即使没有请求,Tomcat 也会保持的空闲线程数。
  • acceptCount:最大排队请求数,当所有线程都在忙时,新的请求会进入这个队列。

总结

  • 普通线程池:主要通过核心线程处理任务,队列排队,最后才扩展到最大线程数。
  • Tomcat 线程池:更注重快速响应,会在核心线程忙时立即创建新线程,直到达到 maxThreads,只有在所有线程都忙时才使用队列来排队请求。

这种差异使得 Tomcat 在处理高并发 Web 请求时更加高效,因为它能够迅速扩展线程池以应对突然增加的请求负载,同时保持合理的空闲线程数来应对新请求。

Java 自定义线程池 Tomcat 线程池在功能上都用于管理线程资源以提高并发性能,但在实现方式、配置策略以及应用场景上有显著区别。 ### 工作机制线程调度方式 Java 自定义线程池通常基于 `ThreadPoolExecutor` 实现,其工作流程遵循标准的线程池模式:首先使用核心线程(corePoolSize)处理任务,当核心线程满载时,将任务放入队列中等待;只有当队列已满且未达到最大线程数(maximumPoolSize)时,才会创建新的非核心线程来处理任务。这种方式更注重资源的合理利用任务调度的灵活性[^3]。 Tomcat 线程池则采用了不同的策略,其设计目标是快速响应 HTTP 请求。Tomcat线程池会优先创建新线程来处理请求,直到达到 `maxThreads` 所设定的最大线程数。只有在所有线程都被占用的情况下,才会将请求放入队列中排队等待。这种机制更适合 Web 服务器对突发流量的快速响应需求[^1]。 ### 配置参数对比 Java 自定义线程池的核心参数包括: - `corePoolSize`:核心线程数。 - `maximumPoolSize`:最大线程数。 - `keepAliveTime`:空闲线程存活时间。 - `workQueue`:任务队列。 - `threadFactory`:线程工厂。 - `handler`:拒绝策略。 而 Tomcat 线程池主要涉及以下关键配置: - `maxThreads`:等同于最大线程数,表示 Tomcat 能够创建的最大工作线程数。 - `minSpareThreads`:最小空闲线程数,即使没有请求,也会保持一定数量的空闲线程以应对突发请求。 - `acceptCount`:最大排队请求数,当所有线程都在忙时,新的请求会进入该队列等待执行[^1]。 ### 线程工厂可配置性 Java 自定义线程池可以通过实现 `ThreadFactory` 接口来自定义线程的创建逻辑,例如设置线程名称、优先级、是否为守护线程等。通过自定义线程工厂类,可以实现线程命名的统一管理,并支持参数的可配置化,便于生产环境中不同业务模块的使用。此外,在系统配置值不生效时,可以提供默认值以增强系统的健壮性适应性[^2]。 Tomcat 线程池虽然也支持一定程度的定制化,但其线程创建过程由内部机制控制,开发者通常不需要直接干预线程的创建逻辑。不过,Tomcat 提供了丰富的配置选项来满足不同场景下的性能调优需求。 ### 应用场景差异 Java 自定义线程池适用于通用的并发任务处理,如定时任务、异步处理、批量数据计算等场景。它提供了较高的灵活性,适合需要精细控制线程生命周期任务调度的应用。 Tomcat 线程池专为 Web 容器设计,主要用于处理 HTTP 请求。其优化方向是快速响应客户端请求,尤其在高并发场景下表现优异。因此,它更适合部署在 Web 服务器或应用服务器中,用于支撑 Web 应用的并发处理能力。 --- ```java // 示例:自定义线程工厂类 public class CustomThreadFactory implements ThreadFactory { private final AtomicInteger atomicInteger = new AtomicInteger(1); private final String namePrefix; private final boolean isDaemon; public CustomThreadFactory(String prefix, boolean daemon) { if (StringUtils.isNoneBlank(prefix)) { this.namePrefix = prefix; } else { this.namePrefix = "thread_pool"; } isDaemon = daemon; } @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, namePrefix + "-" + atomicInteger.getAndIncrement()); thread.setDaemon(isDaemon); if (thread.getPriority() != Thread.NORM_PRIORITY) { thread.setPriority(Thread.NORM_PRIORITY); } return thread; } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值