吃透线程池核心原理:从参数设计、拒绝策略到高并发场景终极选型

简介: 本文深入剖析Java线程池底层原理,涵盖核心架构、生命周期、7大参数设计逻辑、执行流程及4种拒绝策略;结合CPU/IO密集型等场景给出生产级配置方案,并提供自定义线程工厂、监控、失败任务持久化等完整实战代码,助你规避OOM、雪崩等高频风险。

在高并发Java系统中,线程池是资源管控与性能优化的核心组件。错误的线程池配置,轻则导致系统吞吐量下降、响应延迟飙升,重则引发OOM、服务雪崩。本文从底层源码逻辑出发,系统拆解线程池的核心设计、执行流程、参数选型与生产级实战方案,帮你彻底打通线程池的任督二脉。

一、线程池的核心本质与顶层架构

1.1 池化思想的核心价值

线程池的本质是池化思想在并发编程中的落地,核心价值体现在四个维度:

  • 降低资源损耗:避免频繁创建、销毁线程带来的CPU与内存开销
  • 提升响应速度:任务到达时可直接复用已有线程,无需等待线程创建
  • 管控资源上限:通过控制最大并发线程数,避免系统资源被无限耗尽
  • 统一监控与调优:提供标准化的指标采集与生命周期管理能力

1.2 JDK线程池顶层架构

JDK线程池的核心实现基于一套分层的接口设计,通过模板方法模式解耦任务提交、执行与生命周期管理,架构关系如下:

各核心组件的职责:

  • Executor:顶层抽象接口,仅定义execute(Runnable)方法,彻底解耦任务提交与任务执行
  • ExecutorService:扩展生命周期管理能力,新增任务提交(submit/invokeAll)、线程池关闭等核心方法
  • AbstractExecutorService:模板方法抽象类,实现submit、invoke等通用任务提交逻辑,下沉公共实现
  • ThreadPoolExecutor:线程池核心实现类,包含线程池的全量核心逻辑,是本文讲解的核心主体
  • ScheduledThreadPoolExecutor:定时任务线程池实现,继承ThreadPoolExecutor,支持延迟、周期任务执行

二、线程池的生命周期

线程池有5个明确的生命周期状态,状态流转严格受控,是线程池安全运行的基础保障,状态流转流程如下:

各状态的核心定义:

  1. RUNNING:正常运行状态,可接受新任务,持续处理队列中的等待任务
  2. SHUTDOWN:温和关闭状态,拒绝新任务提交,继续处理队列中已有的任务
  3. STOP:强制关闭状态,拒绝新任务提交,中断正在执行的任务,清空队列中未执行的任务
  4. TIDYING:清理状态,所有任务已终止,工作线程数归0,即将执行terminated()钩子方法
  5. TERMINATED:终止状态,terminated()方法执行完成,线程池彻底销毁

核心区别:shutdown()会等待所有任务执行完成,适用于正常关闭场景;shutdownNow()会强制中断正在执行的任务,返回未执行的任务列表,适用于紧急停机场景。

三、线程池核心参数全解与设计逻辑

ThreadPoolExecutor的核心构造函数包含7个核心参数,每个参数直接决定线程池的运行行为,是参数调优的核心基础。

public ThreadPoolExecutor(int corePoolSize,
                         int maximumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler)

3.1 corePoolSize 核心线程数

核心线程数是线程池长期保留的存活线程数量,即使线程处于空闲状态,也不会被回收(除非开启allowCoreThreadTimeOut)。

  • 核心设计:核心线程是线程池的常驻线程,用于应对日常流量,避免频繁创建线程
  • 预热机制:可通过prestartCoreThread()预热单个核心线程,prestartAllCoreThreads()预热所有核心线程,解决系统启动时首次请求的延迟问题
  • 超时回收:开启allowCoreThreadTimeOut(true)后,核心线程空闲超过keepAliveTime也会被回收,适用于突发流量后资源释放的场景

3.2 maximumPoolSize 最大线程数

最大线程数是线程池允许创建的最大工作线程数量,是系统资源管控的核心上限。

  • 核心逻辑:只有当核心线程数已满、工作队列也已满时,线程池才会创建新的非核心线程,直到线程数达到maximumPoolSize
  • 高频误区:90%的开发者会错误认为任务执行顺序是「核心线程→最大线程→队列」,正确顺序是「核心线程→队列→最大线程」
  • 失效场景:当使用无界工作队列时,队列永远不会满,maximumPoolSize参数完全失效,永远不会创建非核心线程

3.3 keepAliveTime & unit 空闲线程存活时间

keepAliveTime是非核心线程空闲后的最大存活时间,unit是对应的时间单位。

  • 核心作用:峰值流量过后,回收空闲的非核心线程,释放系统资源
  • 生效范围:默认仅对非核心线程生效,开启allowCoreThreadTimeOut(true)后,对核心线程同样生效
  • 选型建议:短任务场景可设置较小的值,快速释放资源;长任务场景可设置较大的值,避免频繁创建线程

3.4 workQueue 工作队列

工作队列是用于存储等待执行任务的阻塞队列,是线程池调优、流量削峰的核心组件。常用阻塞队列的特性与适用场景如下:

队列类型 核心特性 适用场景 风险点
ArrayBlockingQueue 有界队列,基于数组实现,必须指定容量,FIFO顺序 稳定流量的业务场景,生产环境首选 容量设置过小会频繁触发拒绝策略
LinkedBlockingQueue 基于链表实现,默认容量为Integer.MAX_VALUE(无界),可指定容量 可控流量的任务处理,需严格指定容量 不指定容量时会无限积压任务,引发OOM,导致maximumPoolSize失效
SynchronousQueue 不存储元素的阻塞队列,插入操作必须等待对应移除操作 短任务、突发流量场景,任务直接递交给线程 需配合合理的maximumPoolSize,否则会无限创建线程
DelayedWorkQueue 延迟队列,基于堆实现,支持任务按延迟时间排序 定时任务、延迟重试场景 任务执行时间不可控会导致后续任务延迟
PriorityBlockingQueue 支持优先级的无界阻塞队列,任务按优先级排序执行 有优先级区分的任务场景 无界特性存在OOM风险,低优先级任务可能出现饥饿

3.5 threadFactory 线程工厂

线程工厂用于创建线程池的工作线程,是生产环境排查问题、异常管控的核心组件。

  • 核心价值:为线程设置有意义的名称,问题排查时可快速定位到对应线程池;设置线程优先级、守护线程状态;为线程设置未捕获异常处理器,避免异常被静默吞掉
  • 生产要求:必须自定义线程工厂,禁止使用默认的DefaultThreadFactory,默认工厂生成的线程名称无业务含义,无法定位问题

3.6 handler 拒绝策略

拒绝策略是线程池的流量保护机制,当核心线程、工作队列、最大线程数均已满,无法处理新任务时,会触发预设的拒绝策略,是系统高可用的最后一道防线。

四、线程池核心执行流程

线程池的任务执行逻辑完全由execute()方法定义,是线程池的核心灵魂,执行流程如下:

执行流程的核心细节:

  1. 任务提交后,首先判断当前运行的核心线程数是否小于corePoolSize,若是则创建新的核心线程执行任务
  2. 若核心线程数已满,尝试将任务加入工作队列,入队成功则等待空闲线程执行
  3. 若队列已满,判断当前线程数是否小于maximumPoolSize,若是则创建非核心线程执行任务
  4. 若线程数已达到maximumPoolSize,触发拒绝策略处理新任务

核心补充:execute()submit()的核心区别

  • execute()仅支持提交Runnable任务,无返回值,任务运行时异常会直接抛出,可被异常处理器捕获
  • submit()支持提交Runnable/Callable任务,返回Future对象,任务异常会被封装到Future中,只有调用get()方法时才会抛出异常,不调用则异常会被完全静默吞掉

五、拒绝策略全解与生产级实战

5.1 JDK内置拒绝策略

JDK内置了4种拒绝策略,均实现了RejectedExecutionHandler接口,可直接使用:

  1. AbortPolicy:默认拒绝策略,直接抛出RejectedExecutionException异常,中断任务提交。适用于绝大多数业务场景,可让调用方快速感知任务被拒绝,及时做降级处理
  2. CallerRunsPolicy:调用者运行策略,由提交任务的线程直接执行该任务。不会丢弃任务、不会抛出异常,会阻塞任务提交线程,降低任务提交速度,实现自动流量削峰。适用于日志写入、非核心统计等不允许任务丢失的场景
  3. DiscardPolicy:丢弃策略,直接静默丢弃新提交的任务,不抛出任何异常。仅适用于完全非核心、可丢失的任务,生产环境需谨慎使用
  4. DiscardOldestPolicy:丢弃最老任务策略,移除队列中等待最久的任务,重新提交当前任务。适用于消息通知、最新数据优先的场景,禁止与优先级队列配合使用

5.2 自定义拒绝策略实战

生产环境中,内置拒绝策略往往无法满足业务需求,需自定义拒绝策略实现日志记录、告警、降级、任务持久化等能力。

package com.jam.demo.threadpool;

import com.alibaba.fastjson2.JSON;
import com.jam.demo.entity.FailTask;
import com.jam.demo.mapper.FailTaskMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.util.Date;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

/**
* 自定义业务拒绝策略
* @author ken
*/

@Slf4j
@Component
public class BusinessRejectedExecutionHandler implements RejectedExecutionHandler {

   private final FailTaskMapper failTaskMapper;

   public BusinessRejectedExecutionHandler(FailTaskMapper failTaskMapper) {
       this.failTaskMapper = failTaskMapper;
   }

   /**
    * 拒绝任务处理逻辑
    * @param r 被拒绝的任务
    * @param executor 当前线程池实例
    */

   @Override
   public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
       log.error("任务被线程池拒绝,线程池信息:核心线程数={},最大线程数={},活跃线程数={},队列剩余容量={}",
               executor.getCorePoolSize(),
               executor.getMaximumPoolSize(),
               executor.getActiveCount(),
               executor.getQueue().remainingCapacity());

       try {
           if (!ObjectUtils.isEmpty(r)) {
               FailTask failTask = new FailTask();
               failTask.setTaskContent(JSON.toJSONString(r));
               failTask.setCreateTime(new Date());
               failTask.setTaskStatus(0);
               failTask.setRetryCount(0);
               failTaskMapper.insert(failTask);
               log.info("拒绝任务持久化成功,任务信息:{}", failTask);
           }
       } catch (Exception e) {
           log.error("拒绝任务持久化失败", e);
       }

       throw new RuntimeException("线程池任务被拒绝,已触发降级处理",
               new java.util.concurrent.RejectedExecutionException("Task " + r.toString() +
                       " rejected from " + executor.toString()));
   }
}

六、高并发场景线程池选型与参数配置

线程池没有万能配置,必须结合业务场景的任务类型、流量特征进行设计,核心选型逻辑如下。

6.1 按任务类型选型配置

6.1.1 CPU密集型任务

任务特征:主要消耗CPU资源,如加密解密、数据压缩、数值计算、内存排序等,线程运行时CPU利用率高,等待时间极少。

  • 核心配置公式:核心线程数 = CPU核心数 + 1
  • 设计逻辑:避免过多线程导致频繁的CPU上下文切换,最大化CPU利用率,+1是为了应对线程页缺失、意外暂停导致的CPU空闲
  • 队列选型:ArrayBlockingQueue有界队列,容量不宜过大
  • 拒绝策略:AbortPolicy,快速感知失败

6.1.2 IO密集型任务

任务特征:主要消耗IO资源,如数据库查询、RPC调用、文件读写、网络请求等,线程大部分时间处于等待状态,CPU利用率低。

  • 基础配置公式:核心线程数 = CPU核心数 * 2
  • 精准配置公式:核心线程数 = CPU核心数 * (1 + 平均等待时间 / 平均工作时间)
  • 设计逻辑:线程等待IO时CPU处于空闲状态,可通过更多线程充分利用CPU资源
  • 队列选型:ArrayBlockingQueue有界队列,可设置稍大的容量应对流量波动
  • 拒绝策略:CallerRunsPolicy或自定义降级策略,避免任务丢失

6.1.3 混合型任务

任务特征:同时包含CPU密集与IO密集操作,是绝大多数业务系统的常见场景。

  • 核心方案:线程池隔离,将CPU密集型与IO密集型任务拆分到不同的线程池,避免慢IO任务占用所有线程,导致CPU密集任务无法执行
  • 设计逻辑:不同类型任务的线程数配置、队列选型完全不同,隔离后可独立调优,避免互相影响
  • 进阶方案:核心业务与非核心业务线程池隔离,避免非核心业务异常拖垮核心业务

6.1.4 定时任务场景

任务特征:延迟执行、周期执行的任务,如定时统计、状态同步、超时检查等。

  • 实现选型:ScheduledThreadPoolExecutor,禁止使用Timer(单线程、异常会导致所有任务停止)
  • 核心配置:核心线程数根据任务数量与执行周期设置,通常为CPU核心数,最大线程数固定为Integer.MAX_VALUE(队列特性决定不会触发)
  • 关键要求:任务内部必须捕获所有异常,未捕获的异常会导致后续周期任务完全停止执行
  • 队列选型:内置DelayedWorkQueue,无需自定义

6.2 生产环境选型禁忌

  1. 禁止使用Executors创建线程池:Executors的静态方法均存在OOM风险,newFixedThreadPool/newSingleThreadExecutor使用无界LinkedBlockingQueue,newCachedThreadPool最大线程数为Integer.MAX_VALUE,生产环境必须通过ThreadPoolExecutor手动创建
  2. 禁止使用无界队列:除非能严格控制任务提交数量,否则无界队列会导致任务无限积压,内存占用飙升,最终引发OOM
  3. 禁止所有任务共用一个线程池:不同类型、不同优先级的任务必须做线程池隔离,避免慢任务拖垮整个系统
  4. 禁止将maximumPoolSize设置为Integer.MAX_VALUE:会导致无限创建线程,耗尽系统内存与CPU资源
  5. 禁止忽略线程池关闭:方法内创建的局部线程池,必须在finally中关闭;容器管理的线程池,需通过@PreDestroy注解在容器销毁时关闭,避免线程泄露

七、代码实战

7.1 项目依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

   <modelVersion>4.0.0</modelVersion>
   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>3.2.4</version>
       <relativePath/>
   </parent>
   <groupId>com.jam</groupId>
   <artifactId>threadpool-demo</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>threadpool-demo</name>
   <properties>
       <java.version>17</java.version>
       <guava.version>33.1.0-jre</guava.version>
       <fastjson2.version>2.0.52</fastjson2.version>
       <mybatis-plus.version>3.5.6</mybatis-plus.version>
       <springdoc.version>2.5.0</springdoc.version>
   </properties>
   <dependencies>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springdoc</groupId>
           <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
           <version>${springdoc.version}</version>
       </dependency>
       <dependency>
           <groupId>com.baomidou</groupId>
           <artifactId>mybatis-plus-boot-starter</artifactId>
           <version>${mybatis-plus.version}</version>
       </dependency>
       <dependency>
           <groupId>com.mysql</groupId>
           <artifactId>mysql-connector-j</artifactId>
           <scope>runtime</scope>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>1.18.34</version>
           <scope>provided</scope>
       </dependency>
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-core</artifactId>
       </dependency>
       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>${guava.version}</version>
       </dependency>
       <dependency>
           <groupId>com.alibaba.fastjson2</groupId>
           <artifactId>fastjson2</artifactId>
           <version>${fastjson2.version}</version>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
   </dependencies>
   <build>
       <plugins>
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <configuration>
                   <excludes>
                       <exclude>
                           <groupId>org.projectlombok</groupId>
                           <artifactId>lombok</artifactId>
                       </exclude>
                   </excludes>
               </configuration>
           </plugin>
       </plugins>
   </build>
</project>

7.2 自定义线程工厂实现

package com.jam.demo.threadpool;

import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 自定义业务线程工厂
* @author ken
*/

@Slf4j
public class BusinessThreadFactory implements ThreadFactory {

   private final AtomicInteger threadNumber = new AtomicInteger(1);
   private final String threadNamePrefix;
   private final boolean daemon;
   private final int priority;

   public BusinessThreadFactory(String threadNamePrefix) {
       this(threadNamePrefix, false, Thread.NORM_PRIORITY);
   }

   public BusinessThreadFactory(String threadNamePrefix, boolean daemon, int priority) {
       if (!StringUtils.hasText(threadNamePrefix)) {
           throw new IllegalArgumentException("线程名称前缀不能为空");
       }
       this.threadNamePrefix = threadNamePrefix + "-thread-";
       this.daemon = daemon;
       this.priority = priority;
   }

   @Override
   public Thread newThread(Runnable r) {
       Thread thread = new Thread(r, threadNamePrefix + threadNumber.getAndIncrement());
       thread.setDaemon(daemon);
       thread.setPriority(priority);
       thread.setUncaughtExceptionHandler((t, e) -> {
           log.error("线程{}发生未捕获异常", t.getName(), e);
       });
       return thread;
   }
}

7.3 线程池配置类

package com.jam.demo.config;

import com.jam.demo.threadpool.BusinessRejectedExecutionHandler;
import com.jam.demo.threadpool.BusinessThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* 线程池配置类
* @author ken
*/

@Configuration
public class ThreadPoolConfig {

   private static final int CPU_CORE_SIZE = Runtime.getRuntime().availableProcessors();

   private final BusinessRejectedExecutionHandler rejectedExecutionHandler;

   public ThreadPoolConfig(BusinessRejectedExecutionHandler rejectedExecutionHandler) {
       this.rejectedExecutionHandler = rejectedExecutionHandler;
   }

   /**
    * CPU密集型任务线程池
    */

   @Bean("cpuIntensiveThreadPool")
   public ThreadPoolExecutor cpuIntensiveThreadPool() {
       int corePoolSize = CPU_CORE_SIZE + 1;
       int maximumPoolSize = corePoolSize;
       return new ThreadPoolExecutor(
               corePoolSize,
               maximumPoolSize,
               60L,
               TimeUnit.SECONDS,
               new ArrayBlockingQueue<>(100),
               new BusinessThreadFactory("cpu-intensive"),
               rejectedExecutionHandler
       );
   }

   /**
    * IO密集型任务线程池
    */

   @Bean("ioIntensiveThreadPool")
   public ThreadPoolExecutor ioIntensiveThreadPool() {
       int corePoolSize = CPU_CORE_SIZE * 2;
       int maximumPoolSize = corePoolSize * 2;
       return new ThreadPoolExecutor(
               corePoolSize,
               maximumPoolSize,
               60L,
               TimeUnit.SECONDS,
               new ArrayBlockingQueue<>(500),
               new BusinessThreadFactory("io-intensive"),
               rejectedExecutionHandler
       );
   }

   /**
    * 通用业务线程池
    */

   @Bean("businessThreadPool")
   public ThreadPoolExecutor businessThreadPool() {
       return new ThreadPoolExecutor(
               10,
               20,
               60L,
               TimeUnit.SECONDS,
               new ArrayBlockingQueue<>(200),
               new BusinessThreadFactory("common-business"),
               rejectedExecutionHandler
       );
   }

   /**
    * 定时任务线程池
    */

   @Bean("scheduledThreadPool")
   public java.util.concurrent.ScheduledExecutorService scheduledThreadPool() {
       return new java.util.concurrent.ScheduledThreadPoolExecutor(
               CPU_CORE_SIZE,
               new BusinessThreadFactory("scheduled-task"),
               rejectedExecutionHandler
       );
   }
}

7.4 线程池监控实现

package com.jam.demo.monitor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

/**
* 线程池监控组件
* @author ken
*/

@Slf4j
@Component
public class ThreadPoolMonitor {

   private final Map<String, ThreadPoolExecutor> threadPoolExecutorMap;

   public ThreadPoolMonitor(Map<String, ThreadPoolExecutor> threadPoolExecutorMap) {
       this.threadPoolExecutorMap = threadPoolExecutorMap;
   }

   /**
    * 每分钟采集线程池指标
    */

   @Scheduled(fixedRate = 60 * 1000)
   public void collectThreadPoolMetrics() {
       threadPoolExecutorMap.forEach((poolName, executor) -> {
           log.info("线程池[{}]监控指标:核心线程数={},最大线程数={},当前线程数={},活跃线程数={},队列大小={},队列剩余容量={},已完成任务数={}",
                   poolName,
                   executor.getCorePoolSize(),
                   executor.getMaximumPoolSize(),
                   executor.getPoolSize(),
                   executor.getActiveCount(),
                   executor.getQueue().size(),
                   executor.getQueue().remainingCapacity(),
                   executor.getCompletedTaskCount());

           if (executor.getQueue().remainingCapacity() < 50) {
               log.warn("线程池[{}]队列剩余容量不足,即将触发拒绝策略", poolName);
           }
           if (executor.getActiveCount() >= executor.getMaximumPoolSize() * 0.9) {
               log.warn("线程池[{}]活跃线程数已超过最大线程数90%,系统负载过高", poolName);
           }
       });
   }
}

7.5 线程池使用与异常处理示例

package com.jam.demo.controller;

import com.google.common.collect.Lists;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

/**
* 线程池使用示例接口
* @author ken
*/

@Slf4j
@RestController
@RequestMapping("/threadpool")
@Tag(name = "线程池示例接口", description = "线程池使用与异常处理示例")
public class ThreadPoolDemoController {

   private final ThreadPoolExecutor ioIntensiveThreadPool;

   public ThreadPoolDemoController(ThreadPoolExecutor ioIntensiveThreadPool) {
       this.ioIntensiveThreadPool = ioIntensiveThreadPool;
   }

   @GetMapping("/execute/demo")
   @Operation(summary = "execute方法使用示例", description = "execute方法提交任务,异常直接抛出")
   public String executeDemo() {
       ioIntensiveThreadPool.execute(() -> {
           log.info("execute方法执行任务开始");
           int result = 1 / 0;
           log.info("execute方法执行任务结束,结果:{}", result);
       });
       return "任务提交成功";
   }

   @GetMapping("/submit/demo")
   @Operation(summary = "submit方法使用示例", description = "submit方法提交任务,异常正确处理方式")
   public String submitDemo() {
       List<Future<Integer>> futureList = Lists.newArrayList();
       for (int i = 0; i < 5; i++) {
           int finalI = i;
           Future<Integer> future = ioIntensiveThreadPool.submit(() -> {
               log.info("submit方法执行任务{}开始", finalI);
               if (finalI == 3) {
                   throw new RuntimeException("任务执行异常");
               }
               return finalI * 2;
           });
           futureList.add(future);
       }

       if (!CollectionUtils.isEmpty(futureList)) {
           futureList.forEach(future -> {
               try {
                   Integer result = future.get();
                   log.info("任务执行结果:{}", result);
               } catch (Exception e) {
                   log.error("任务执行异常", e);
               }
           });
       }
       return "任务提交成功";
   }

   @GetMapping("/dynamic/adjust")
   @Operation(summary = "线程池参数动态调整", description = "生产环境动态调整线程池核心参数")
   public String dynamicAdjust() {
       ioIntensiveThreadPool.setCorePoolSize(20);
       ioIntensiveThreadPool.setMaximumPoolSize(40);
       ioIntensiveThreadPool.setKeepAliveTime(30, java.util.concurrent.TimeUnit.SECONDS);
       log.info("线程池参数动态调整完成");
       return "参数调整成功";
   }
}

7.6 失败任务持久化实体与Mapper

package com.jam.demo.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.util.Date;

/**
* 失败任务实体
* @author ken
*/

@Data
@TableName("t_fail_task")
@Schema(description = "失败任务实体")
public class FailTask {

   @TableId(type = IdType.AUTO)
   @Schema(description = "主键ID")
   private Long id;

   @Schema(description = "任务内容")
   private String taskContent;

   @Schema(description = "创建时间")
   private Date createTime;

   @Schema(description = "任务状态 0-待重试 1-重试成功 2-重试失败")
   private Integer taskStatus;

   @Schema(description = "重试次数")
   private Integer retryCount;

   @Schema(description = "最后重试时间")
   private Date lastRetryTime;
}

package com.jam.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jam.demo.entity.FailTask;
import org.apache.ibatis.annotations.Mapper;

/**
* 失败任务Mapper
* @author ken
*/

@Mapper
public interface FailTaskMapper extends BaseMapper<FailTask> {
}

对应的MySQL表结构:

CREATE TABLE `t_fail_task` (
 `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
 `task_content` text COMMENT '任务内容',
 `create_time` datetime NOT NULL COMMENT '创建时间',
 `task_status` tinyint NOT NULL DEFAULT '0' COMMENT '任务状态 0-待重试 1-重试成功 2-重试失败',
 `retry_count` int NOT NULL DEFAULT '0' COMMENT '重试次数',
 `last_retry_time` datetime DEFAULT NULL COMMENT '最后重试时间',
 PRIMARY KEY (`id`),
 KEY `idx_task_status` (`task_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='线程池失败任务表';

八、高频误区与避坑指南

  1. 执行顺序误区:错误认为任务执行顺序是「核心线程→最大线程→队列」,正确顺序是「核心线程→队列→最大线程」,队列满了才会触发非核心线程创建
  2. 线程数设置误区:认为核心线程数越大,系统吞吐量越高。实际线程数超过CPU核心数后,会引发频繁的上下文切换,导致吞吐量下降、延迟升高
  3. 无界队列误区:使用无界LinkedBlockingQueue不指定容量,导致任务无限积压引发OOM,同时maximumPoolSize参数完全失效
  4. 异常处理误区:使用submit()提交任务时,不调用Future.get()方法,导致任务异常被静默吞掉,无法感知业务错误
  5. 线程池共用误区:所有业务共用一个线程池,慢IO任务占用所有线程,导致核心业务任务无法执行,引发系统雪崩
  6. 容器环境误区:容器环境下直接使用Runtime.getRuntime().availableProcessors()获取CPU核心数,会获取到宿主机的核心数,导致线程数设置过大,需使用容器感知的CPU核心数获取方式
  7. 定时任务误区:定时任务未捕获异常,导致单个任务异常后,后续所有周期任务完全停止执行
  8. 线程池关闭误区:方法内创建的局部线程池未关闭,导致线程泄露,最终耗尽系统资源

九、总结

线程池的本质是资源管控,而非单纯的提升性能。正确使用线程池,需要深入理解其底层执行逻辑,结合业务场景的任务类型、流量特征设计参数,做好线程池隔离、异常处理、监控告警与拒绝策略降级。没有万能的线程池配置,只有贴合业务的最优设计。只有吃透底层原理,才能在高并发场景中设计出稳定、高效的线程池方案,避免踩坑,保障系统的高可用。

目录
相关文章
|
19天前
|
存储 算法 关系型数据库
吃透分库分表:分片策略、跨库事务与平滑扩容全解
本文系统讲解MySQL分库分表核心实践:涵盖垂直/水平拆分原理、哈希取模/一致性哈希/范围/枚举/复合五大分片策略、XA强一致与TCC/事务消息等最终一致性方案、双倍停机与预分片无停机扩容,以及分布式ID、避坑指南等关键要点。
205 3
|
SQL 算法 Java
(二十六)MySQL分库篇:Sharding-Sphere分库分表框架的保姆级教学!
前面《MySQL主从原理篇》、《MySQL主从实践篇》两章中聊明白了MySQL主备读写分离、多主多写热备等方案,但如果这些高可用架构依旧无法满足业务规模,或业务增长的需要,此时就需要考虑选用分库分表架构。
7182 4
|
Cloud Native Java Go
Springboot 获取 /resources 目录资源文件的 9 种方法
Springboot 获取 /resources 目录资源文件的 9 种方法
4618 0
|
21天前
|
存储 监控 Java
Java 线程模型底层解密:从内核原理到生产级架构选型,全链路实战指南
本文深入剖析Java线程模型底层原理:从OS线程模型(ULT/KLT/混合)、HotSpot 1:1内核线程实现,到线程生命周期、wait/park中断机制、ThreadLocal内存泄漏规避;详解线程池参数选型(CPU/IO密集型)、生产级最佳实践,并对比虚拟线程优势与适用场景,打通原理到落地全链路。
282 1
Java 线程模型底层解密:从内核原理到生产级架构选型,全链路实战指南
|
JSON 移动开发 数据可视化
我和JSON Schema的那些事
哈喽,我是🌲 树酱。今天聊一聊关于我跟Json schema的一些交集,顺便给大家重新梳理下今日这个主角的概念及当下主要的一些应用场景
892 0
我和JSON Schema的那些事
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
4月前
|
存储 缓存 监控
JDK自带调优五件套(Jstat/Jinfo/Jmap/Jhat/Jstack)深度解析+实战指南
本文深入解析JDK自带的5款JVM调优工具(Jstat、Jinfo、Jmap、Jhat、Jstack),帮助开发者高效定位线上系统性能问题。Jstat实时监控GC状态,Jinfo查看修改JVM参数,Jmap生成内存快照,Jhat分析堆内存泄漏,Jstack诊断线程死锁。通过企业级实战案例,展示工具协同使用流程,并给出缓存优化等解决方案。这些轻量级工具无需额外部署,是Java开发者必备的性能调优利器,能有效应对内存泄漏、CPU过载等常见问题。
1367 3
|
缓存 Java Maven
微服务技术系列教程(07) - SpringBoot - 缓存的使用
微服务技术系列教程(07) - SpringBoot - 缓存的使用
244 0
|
缓存 NoSQL Java
微服务 Spring Boot 整合Redis 实战开发解决高并发数据缓存
高并发场景下,如何巧妙的利用缓存解决,提高系统的可用性? Redis 缓存来搞定!
652 0
微服务 Spring Boot 整合Redis 实战开发解决高并发数据缓存