学习分享-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 请求时更加高效,因为它能够迅速扩展线程池以应对突然增加的请求负载,同时保持合理的空闲线程数来应对新请求。

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值