学习分享-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)示例
java 复制代码
ExecutorService executorService = new ThreadPoolExecutor(
    10, // corePoolSize
    20, // maximumPoolSize
    60L, // keepAliveTime
    TimeUnit.SECONDS, // unit
    new LinkedBlockingQueue<Runnable>(100) // workQueue
);
Tomcat 线程池配置示例(在server.xml中)
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 请求时更加高效,因为它能够迅速扩展线程池以应对突然增加的请求负载,同时保持合理的空闲线程数来应对新请求。

相关推荐
xlsw_2 小时前
java全栈day20--Web后端实战(Mybatis基础2)
java·开发语言·mybatis
神仙别闹3 小时前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭3 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫3 小时前
泛型(2)
java
超爱吃士力架3 小时前
邀请逻辑
java·linux·后端
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石4 小时前
12/21java基础
java
李小白664 小时前
Spring MVC(上)
java·spring·mvc
sanguine__4 小时前
Web APIs学习 (操作DOM BOM)
学习
GoodStudyAndDayDayUp4 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea