最后一篇[关于线程池的详细讲解]

190 阅读4分钟

一、线程池基础概念

为什么需要线程池

  • 复用线程资源,减少创建/销毁开销
  • 控制并发线程数量,防止资源耗尽
  • 统一管理任务队列和线程生命周期

二、Spring Boot线程池配置

1. 添加基础依赖(Spring Boot Starter Web)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

2. 配置线程池(推荐使用ThreadPoolTaskExecutor

@Configuration
@EnableAsync // 启用异步支持
public class ThreadPoolConfig {

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数(默认保持活跃的线程数)
        executor.setCorePoolSize(5);
        // 最大线程数(队列满后能创建的最大线程数)
        executor.setMaxPoolSize(10);
        // 队列容量(超过核心线程数时,新任务进入队列等待)
        executor.setQueueCapacity(100);
        // 线程名前缀
        executor.setThreadNamePrefix("async-task-");
        // 拒绝策略(当队列和最大线程都满时的处理方式)
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        executor.initialize();
        return executor;
    }
}

3. application.yml配置(可选)

async:
  thread:
    core-pool-size: 5
    max-pool-size: 10
    queue-capacity: 100

三、使用线程池的4种业务场景

场景1:异步日志记录

@Service
public class LogService {
    @Async("taskExecutor") // 指定使用哪个线程池
    public void asyncAddLog(String logContent) {
        // 模拟耗时操作
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 记录日志:" + logContent);
    }
}

// 在Controller中使用
@RestController
public class LogController {
    @Autowired
    private LogService logService;

    @GetMapping("/log") 
    public String log(){
        logService.asyncAddLog("进来了");
        return "成功";
    }
}

image.png

image.png

场景2:批量数据处理(如Excel导入)

@Service
public class BatchProcessService {

    @Async("taskExecutor")
    public CompletableFuture<String> processDataChunk(List<Data> chunk) {
        // 处理数据分片
        chunk.forEach(data -> {
            // 数据清洗、校验、存储等操作
        });
        return CompletableFuture.completedFuture("分片处理完成");
    }
}

// 调用方示例
public void processAllData(List<Data> allData) {
    // 将数据拆分为10个一组
    List<List<Data>> chunks = Lists.partition(allData, 10);
    
    List<CompletableFuture<String>> futures = chunks.stream()
            .map(chunk -> batchProcessService.processDataChunk(chunk))
            .collect(Collectors.toList());
    
    // 等待所有任务完成
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}

场景3:定时任务增强

@Slf4j
@Service
public class ScheduledService {

    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Scheduled(cron = "0 0/5 * * * ?")
    public void scheduledTask() {
        // 将定时任务提交到线程池
        taskExecutor.execute(() -> {
            log.info("开始执行定时统计任务...");
            // 执行耗时统计逻辑
        });
    }
}

场景4:高并发请求处理

@Service
public class PaymentService {

    @Async("taskExecutor")
    public CompletableFuture<Boolean> asyncPayment(PaymentRequest request) {
        // 模拟支付处理
        boolean success = paymentGateway.process(request);
        return CompletableFuture.completedFuture(success);
    }
}

// Controller中并发处理支付请求
@PostMapping("/batch-pay")
public ResponseEntity<?> batchPay(@RequestBody List<PaymentRequest> requests) {
    List<CompletableFuture<Boolean>> futures = requests.stream()
            .map(paymentService::asyncPayment)
            .collect(Collectors.toList());

    List<Boolean> results = futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList());

    return ResponseEntity.ok(results);
}

四、关键配置参数说明

参数说明
corePoolSize核心线程数,即使空闲也不会被回收
maxPoolSize最大线程数,当队列满时才会创建新线程
queueCapacity任务队列容量,推荐使用有界队列防止内存溢出
keepAliveSeconds非核心线程的空闲存活时间(秒)
RejectedPolicy拒绝策略(AbortPolicy-抛出异常/CallerRunsPolicy-用调用者线程执行)

五、高级功能扩展

1. 添加基础依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

2. 异常处理

@Configuration
public class AsyncExceptionConfig implements AsyncConfigurer {

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex, method, params) -> {
            System.err.println("异步任务异常:" + ex.getMessage());
            System.err.println("方法名称:" + method.getName());
        };
    }
}

3. 线程池监控

// 暴露执行器端点
@Endpoint(id = "thread-pool")
@Component
public class ThreadPoolEndpoint {
    
    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor executor;

    @ReadOperation
    public Map<String, Object> threadPoolStatus() {
        return Map.of(
            "activeCount", executor.getActiveCount(),
            "poolSize", executor.getPoolSize(),
            "queueSize", executor.getThreadPoolExecutor().getQueue().size()
        );
    }
}

// 在Controller中使用
@RestController
public class LogController {
    @Autowired
    private LogService logService;

    @Autowired
    private ThreadPoolEndpoint threadPoolEndpoint;

    @GetMapping("/log") 
    public String log(){
        logService.asyncAddLog("进来了");
        return "成功";
    }

    @GetMapping("/log2")
    public String log2(){
        return JSON.toJSONString(threadPoolEndpoint.threadPoolStatus());
    }
}

image.png

image.png

六、监控数据的使用场景

1. 实时诊断资源瓶颈

  • 队列持续增长:说明处理速度跟不上任务提交速度:

    • 增加maxPoolSize
    • 优化任务处理逻辑
    • 扩容服务器资源
  • 频繁触发拒绝策略

    • 调整queueCapacity
    • 检查任务提交是否合理

2. 动态调整参数(高级)

结合Spring Cloud Config实现动态调整:

@RefreshScope
@Bean
public ThreadPoolTaskExecutor taskExecutor(
    @Value("${thread.pool.core-size:5}") int coreSize,
    @Value("${thread.pool.max-size:10}") int maxSize
) {
    // 创建线程池...
}

3. 设置报警规则(示例)

在Grafana中设置报警规则:

  • thread_pool_queue_size > 80持续5分钟时触发报警
  • thread_pool_rejected_total > 0时立即报警

通过以上方案,就全面掌握线程池的运行状态,及时发现并处理以下典型问题:

  • 队列持续积压导致的延迟升高
  • 线程资源耗尽导致的拒绝请求
  • 配置参数不合理导致的资源浪费

七、生产环境最佳实践

  1. 监控指标选择

    • 必须监控:队列大小、活跃线程数、拒绝次数
    • 建议监控:任务平均耗时、95分位耗时
  2. 安全控制

    management:
      endpoint:
        thread-pool:
          enabled: true
      endpoints:
        web:
          exposure:
            include: health,info,thread-pool
      server:
        port: 9001 # 与管理端口分离
    

八、常见问题排查

  1. 配置不生效

    • 检查是否添加了@EnableAsync注解
    • 确保异步方法在Spring管理的Bean中调用
  2. 线程池参数调优

    • CPU密集型任务:推荐线程数 = CPU核数 + 1
    • IO密集型任务:推荐线程数 = CPU核数 * 2
  3. 资源耗尽问题

    • 监控队列堆积情况(queueSize)
    • 合理设置拒绝策略

九、最佳实践建议

  1. 为不同业务类型配置独立的线程池(如订单线程池、日志线程池)
  2. 使用ThreadPoolTaskExecutor而不是直接使用Java原生线程池
  3. 生产环境建议开启线程池监控
  4. 合理设置任务超时时间

通过以上配置和示例代码,就可以在Spring Boot中高效地使用线程池来处理异步任务和并发请求。

实际使用时需要根据具体业务场景调整线程池参数,并通过监控持续优化。

感谢观看到这里,收藏一下,以后直接用吧,恭喜你又掌握或牢固了一个知识点。