Spring内置常见线程池配置及相关概念

1-线程池

线程池 = 线程的"复用工厂 + 任务调度系统",只是如何用有限线程,稳定地处理无限任务

2-为什么一定要线程池

当你不用线程池的时候:

  • 每个任务一个线程(new Thread),后果:任务多 → 线程多 → 上下文切换爆炸 / 内存耗尽 / OOM
  • 少量线程串行,后果:吞吐低 / 延迟高 / 系统假死

使用线程池只是在吞吐、延迟、资源之间做一个平衡

3-具体实现

使用的类为:

java 复制代码
java.util.concurrent.ThreadPoolExecutor

构造参数:

java 复制代码
public ThreadPoolExecutor(
    int corePoolSize,// 核心线程数
    int maximumPoolSize,// 最大线程数
    long keepAliveTime,// 存活时间
    TimeUnit unit,// 时间单位
    BlockingQueue<Runnable> workQueue,// 任务队列
    ThreadFactory threadFactory,// 线程创建时的规则
    RejectedExecutionHandler handler// 拒绝策略
)

构造参数释义:

  • corePoolSize(核心线程数):线程池"常驻"的线程数量

  • workQueue(任务队列):决定线程池的行为

    常见队列类型为:

    队列 特点 后果
    LinkedBlockingQueue 无界 OOM风险
    ArrayBlockingQueue 有界 可控
    SynchronousQueue 不存任务 压力直接给线程
    PriorityBlockingQueue 优先级 顺序复杂
  • maximumPoolSize(最大线程数):线程池允许创建的最大线程数量

  • keepAliveTime(存活时间):非核心线程,空闲多久会被回收

  • RejectedExecutionHandler(拒绝策略):

    常见策略为:

    策略 行为
    AbortPolicy 抛异常(默认)
    CallerRunsPolicy 调用方线程执行(常用)
    DiscardPolicy 直接丢弃
    DiscardOldestPolicy 丢队列最老任务
  • threadFactory(线程的生产规则):

    java 复制代码
    // 线程池自己并不直接 new Thread(),会通过下方代码创建线程:
    threadFactory.newThread(runnable)
    // threadFactory 最重要的 4 个作用
      new ThreadFactoryBuilder()
        // 1.线程命名
        .setNameFormat("order-exec-%d") // 自动生成 order-exec-0, order-exec-1...
        // 2.设置是否为守护线程:一般主要的业务用,设置为false
        .setDaemon(false)
        // 3.设置优先级(使用较少)
        .setPriority(Thread.NORM_PRIORITY)
    	// 4.设置 UncaughtExceptionHandler:防止线程因异常而终止但不输出任何信息,处理未捕获的异常
    	.setUncaughtExceptionHandler((t, e) -> {
        	log.error("线程 {} 异常", t.getName(), e);
    	});

4-线程池特点

  1. 单个线程池实例,全局只初始化一次

  2. 线程池的数量全局不止一个:可以分出很多个,比如:定时任务线程池,web 容器线程池(Tomcat)等等。主要是控制允许多少个任务同时处于运行态/可运行态,设计线程池的大小需要根据设备为准

  3. 池的大小分为两种类型:

    • CPU密级型:很少阻塞、一直占CPU;线程基本都在使用cpu,所以线程数量基本契合cpu的配置即可

      复制代码
      线程数 ≈ CPU 核数 或 CPU 核数 + 1
      比如: 12 核 16 线程的服务器
      设置:12 ~ 13
    • IO密集型:大量等待、CPU空闲;所以线程数量多一些,尽量高效提高cpu利用率,但不能太多

      复制代码
      线程数 ≈ CPU核数 × (1 + IO等待时间 / 计算时间)
      简化:CPU 核数 × 2 ~ 4
      比如: 12 核 16 线程的服务器
      设置:24 ~ 48

5-线程池一般的分类

类型 是否 CPU 密集 示例
Web 请求 IO Controller
业务异步 混合 @Async
定时任务 IO / 混合 @Scheduled
CPU 密集 加解密

6-Spring内部存在的线程池

6-1.Web容器线程池(最重要)

复制代码
#Tomcat(默认)
server.tomcat.threads.max = 200

特点:

  • IO密集型
  • 每个HTTP请求一个线程
  • 线程大多数时间在等DB/Redis

6-2.定时任务线程池

java 复制代码
@Scheduled

默认:

  • 单线程
  • 所有定时任务串行

一般必须自己配

复制代码
TaskScheduler

6-3.@Async线程池

java 复制代码
@Async
// 每次 @Async 都可能新建一个线程,在默认配置下,本质行为 ≈ new Thread()

默认:

  • SimpleAsyncTaskExecutor
  • 不是真正线程池
  • 会无限创建线程
  • 生产环境必须自定义

PS:Redis/DB等外部中间件连接池的设置大小尽量要契合,关注 max-active 等连接数参数,确保它能匹配你的业务并发量

7-自己建立线程池的条件

凡是:异步、定时、非 HTTP 主流程都应该用"自定义的线程池"

8-新项目线程池调整

一个新的spring项目,有一些情况下需要调整

8-1.Tomcat线程池

默认情况:

yaml 复制代码
maxThreads = 200
minSpareThreads = 10
#Tomcat 线程数 ≈ 下游连接池总容量 × 1~2

示例:

yaml 复制代码
server:
  tomcat:
    threads:
      max: 200               # 最大工作线程数
      min-spare: 20          # 最小空闲线程
      max-connections: 10000 # 最大连接数(含等待)
      accept-count: 100      # 等待队列长度
    connection-timeout: 20000 # 连接超时(ms)

一般经验:

yaml 复制代码
# CPU密集型应用(计算为主)
最大线程数 ≈ CPU核心数 × (1~2)

# IO密集型应用(数据库、网络调用多)
最大线程数 ≈ CPU核心数 × (2~4) × (1 + 等待时间/处理时间)

# 混合型(通用经验值)
最大线程数 = CPU核心数 × 4
最小空闲线程 = CPU核心数 × 2

# 1核2G(低配)
server:
  tomcat:
    threads:
      max: 50-100
      min-spare: 5-10

# 2核4G(标准)
server:
  tomcat:
    threads:
      max: 100-150
      min-spare: 10-20

# 4核8G(中配)
server:
  tomcat:
    threads:
      max: 150-200    # 默认值刚好适合
      min-spare: 20-30

# 8核16G+(高配)
server:
  tomcat:
    threads:
      max: 200-400    # 需要调高
      min-spare: 30-50

8-2.@Scheduled定时任务

默认行为:所有 @Scheduled,共用 1 个线程,串行执行,一般情况下必改

java 复制代码
@Configuration
@EnableScheduling// 启用定时任务功能,允许使用@Scheduled注解
public class ScheduleConfig {

    // Spring会自动使用这个自定义的调度器而不是默认的
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(4);// 设置线程池大小为4个线程(通常建议2-8个)
        scheduler.setThreadNamePrefix("sched-");// 设置线程名称前缀,便于调试识别
        scheduler.setAwaitTerminationSeconds(30);// 设置关闭时等待时间30秒
        scheduler.setWaitForTasksToCompleteOnShutdown(true);// 应用关闭时等待任务执行完成
        return scheduler;
    }
}

8-3.@Async异步线程池

默认 @Async = 什么?

java 复制代码
SimpleAsyncTaskExecutor ≈ new Thread()

自定义异步线程池:

java 复制代码
@Configuration
@EnableAsync// 启用异步方法执行功能,配合 @Async 注解使用
public class AsyncConfig {

    @Bean("asyncExecutor")// 将方法返回的对象注册为名为 "asyncExecutor" 的 Spring Bean
    public Executor asyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(16);// 核心线程数
        executor.setMaxPoolSize(32);// 最大线程数:线程池允许的最大线程数量
        executor.setQueueCapacity(500);// 队列容量:任务队列最大容量,超出后创建新线程
        executor.setThreadNamePrefix("async-");// 创建的线程名称前缀,便于调试识别
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 当线程池满载时,由调用线程自己执行任务,起到缓压作用
        executor.initialize();
        return executor;
    }
}

// 具体使用:
@Async("asyncExecutor")
public void doSomething() {
    // 耗时操作将在独立线程中执行
}

线程分配策略:

  1. 优先使用核心线程: 初始阶段先创建16个核心线程
  2. 队列缓冲机制: 新任务先放入容量为500的任务队列
  3. 动态扩容: 队列满了才创建超过核心线程数的新线程
  4. 上限控制: 线程总数不超过32个

执行流程:

  • 任务量 ≤ 16个 → 使用核心线程
  • 任务量 > 16个 → 先排队等待
  • 队列满(>500个任务)→ 创建新线程直到达到32个上限
  • 达到上限后 → 调用者线程直接执行任务(拒绝策略)

8-4.数据库 / Redis

对齐连接数:

yaml 复制代码
# 数据库(HikariCP)
spring:
  datasource:
    hikari:
      maximum-pool-size: 10 ~ 20

# Redis(Lettuce)
spring:
  redis:
    lettuce:
      pool:
        max-active: 8 ~ 16
#数据库连接数 = min(数据库限制 ÷ 实例数, CPU核心数 × 10)
#Redis连接数 = CPU核心数 × 2

8-5.其他

其实正常项目还会创建一些其他的线程池,除了Spring自带的Tomcat、定时任务、异步任务的线程池,一般还会创建:

  • 通用业务异步线程池,1个,用于:通知、事件、异步处理

  • 外部第三方接口线程池,1~2个,特点:慢、不稳定、有并发上限

  • CPU 密集型线程池,0或1个,用于:加密、大计算、规则引擎

  • 后台/低优先级线程池,1 个,用于:日志、统计、对实时性不敏感任务

9-多线程池好处

现在的系统中使用一个池,管理所有任务也是不可接受的

现在的系统一般都是使用多个线程池:

  • 资源隔离:

    bash 复制代码
    如果你只有一个线程池:
    第三方慢 → 线程占满 → HTTP 请求进不来 → 整个服务不可用
    --------------------
    线程池隔离之后:
    第三方池满 → 第三方失败
    主业务照常
  • 失败可控:

    复制代码
    有界线程池+拒绝策略:返回错误、快速失败、触发降级
  • 可观测性:

    bash 复制代码
    多个线程池意味着:
    async-biz-queue 爆了
    third-party-pool 耗尽
    cpu-pool 满负载
    --------------
    而不是:
    系统很慢,不知道为什么
相关推荐
Assby1 分钟前
Java开发者学习Go语言:Go开发和Java开发的一些区别
后端·go
Thomas.Sir1 分钟前
SpringMVC 工作原理深入解析
spring·设计模式·mvc·spring mvc
吃吃喝喝小朋友2 分钟前
Django Admin后台系统
后端·python·django
StackNoOverflow3 分钟前
Maven 核心知识整理
java·maven
ekkcole3 分钟前
easyexcel2.2.10版本对本地文件指定行或多行样式处理
java·easyexcel
小七mod5 分钟前
【Nacos】Nacos1.4.x服务注册源码分析
java·spring cloud·微服务·nacos·源码·集群·注册中心
于先生吖9 分钟前
Java 打车小程序 APP 源码 顺风车滴滴跑腿系统完整实现
java·开发语言·打车系统
树獭叔叔10 分钟前
检索增强生成(RAG):让大模型突破知识边界
后端·aigc·openai
凌冰_11 分钟前
IDEA2025 基于 Jakarta EE 开发 Servlet + Thymeleaf
java·servlet
南囝coding14 分钟前
OpenClaw 到底能干什么?可以看看这 60 个真实用例
前端·后端