SpringBoot 自定义线程池 + 实时监控指标

1 核心知识点回顾

JDK原生线程池核心构造参数

java 复制代码
new ThreadPoolExecutor(
    corePoolSize,        // 核心线程数
    maximumPoolSize,     // 最大线程数
    keepAliveTime,      // 非核心线程空闲存活时间
    unit,
    workQueue,          // 阻塞队列
    handler             // 拒绝策略
);

监控要采集哪些核心指标

  1. 核心/最大/当前活跃线程数
  2. 队列总容量、队列当前积压任务数
  3. 已完成任务总数、提交总任务数
  4. 拒绝任务数量
  5. 任务平均执行耗时、队列等待耗时

2、 Maven依赖

SpringBoot2.x/3.x内置Micrometer,配合Actuator暴露监控端点,Prometheus+Grafana可视化;

xml 复制代码
<!-- web项目基础依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 监控端点暴露 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- prometheus格式指标输出 -->
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

3、 yml配置

yaml 复制代码
spring:
  application:
    name: custom-thread-pool-demo

# 暴露全部actuator端点
management:
  endpoints:
    web:
      exposure:
        include: '*'
  metrics:
    export:
      prometheus:
        enabled: true
  endpoint:
    health:
      show-details: always

# 自定义线程池业务配置
thread-pool:
  custom:
    core-size: 5
    max-size: 10
    queue-capacity: 100
    keep-alive-seconds: 60

4、 自定义线程池配置类 + 注册Micrometer监控指标

4.1 配置属性绑定类

java 复制代码
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@Component
@ConfigurationProperties(prefix = "thread-pool.custom")
public class CustomThreadPoolProperties {
    private Integer coreSize;
    private Integer maxSize;
    private Integer queueCapacity;
    private Long keepAliveSeconds;
}

4.2 线程池配置 + 指标注册

java 复制代码
import io.micrometer.core.instrument.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;

@Configuration
public class ThreadPoolConfig {

    private final CustomThreadPoolProperties poolProps;
    private final MeterRegistry meterRegistry;

    public ThreadPoolConfig(CustomThreadPoolProperties poolProps, MeterRegistry meterRegistry) {
        this.poolProps = poolProps;
        this.meterRegistry = meterRegistry;
    }

    /**
     * 自定义业务线程池
     */
    @Bean("businessThreadPool")
    public Executor businessThreadPool() {
        // 1. 构造原生JDK线程池
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(poolProps.getQueueCapacity());
        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                poolProps.getCoreSize(),
                poolProps.getMaxSize(),
                poolProps.getKeepAliveSeconds(),
                TimeUnit.SECONDS,
                queue,
                new CustomThreadFactory("business-pool-"),
                handler
        );

        // 2. 注册所有监控指标到micrometer
        registerThreadPoolMetrics(executor, "business_thread_pool");

        return executor;
    }

    /**
     * 把线程池所有运行指标注册到监控系统
     */
    private void registerThreadPoolMetrics(ThreadPoolExecutor executor, String poolName) {
        Tags tags = Tags.of("pool_name", poolName);

        // 1. 静态配置指标
        Gauge.builder("thread.pool.core.size", executor, ThreadPoolExecutor::getCorePoolSize)
                .tags(tags)
                .register(meterRegistry);
        Gauge.builder("thread.pool.max.size", executor, ThreadPoolExecutor::getMaximumPoolSize)
                .tags(tags)
                .register(meterRegistry);
        Gauge.builder("thread.pool.queue.capacity", () -> poolProps.getQueueCapacity())
                .tags(tags)
                .register(meterRegistry);

        // 2. 动态实时运行指标
        // 当前活跃线程数
        Gauge.builder("thread.pool.active.threads", executor, ThreadPoolExecutor::getActiveCount)
                .tags(tags)
                .register(meterRegistry);
        // 队列剩余容量
        Gauge.builder("thread.pool.queue.remaining", queue -> queue.remainingCapacity(), executor.getQueue())
                .tags(tags)
                .register(meterRegistry);
        // 队列积压任务数
        Gauge.builder("thread.pool.queue.size", queue -> queue.size(), executor.getQueue())
                .tags(tags)
                .register(meterRegistry);
        // 已完成任务总数
        Gauge.builder("thread.pool.completed.task.count", executor, ThreadPoolExecutor::getCompletedTaskCount)
                .tags(tags)
                .register(meterRegistry);
        // 总提交任务数
        Gauge.builder("thread.pool.total.task.count", executor, ThreadPoolExecutor::getTaskCount)
                .tags(tags)
                .register(meterRegistry);

        // 3. 拒绝任务计数器
        AtomicLong rejectCount = new AtomicLong(0);
        executor.setRejectedExecutionHandler((r, e) -> {
            rejectCount.incrementAndGet();
            handler(r, e);
        });
        FunctionCounter.builder("thread.pool.reject.count", rejectCount, AtomicLong::get)
                .tags(tags)
                .register(meterRegistry);
    }

    /**
     * 自定义线程工厂:指定线程前缀,方便日志排查
     */
    static class CustomThreadFactory implements ThreadFactory {
        private final String prefix;
        private final AtomicLong num = new AtomicLong(0);

        public CustomThreadFactory(String prefix) {
            this.prefix = prefix;
        }

        @Override
        public Thread newThread(Runnable r) {
            String threadName = prefix + num.getAndIncrement();
            Thread t = new Thread(r, threadName);
            t.setDaemon(false);
            return t;
        }
    }
}

5、 测试线程池使用

5.1 注入线程池执行业务任务

java 复制代码
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Random;

@Component
public class ThreadPoolTestTask {

    @Resource(name = "businessThreadPool")
    private Executor threadPool;

    private final Random random = new Random();

    // 定时不断提交任务,制造队列积压、线程活跃
    @Scheduled(fixedRate = 200)
    public void submitTask() {
        threadPool.execute(() -> {
            try {
                // 模拟业务耗时 100~500ms
                Thread.sleep(100 + random.nextInt(400));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

6、 查看监控指标

6.1 Prometheus指标地址

启动项目后访问:

复制代码
http://127.0.0.1:8080/actuator/prometheus

可以直接抓取所有线程池实时指标,示例指标片段:

复制代码
# HELP thread_pool_active_threads Gauge measuring active threads of thread pool
# TYPE thread_pool_active_threads gauge
thread_pool_active_threads{pool_name="business_thread_pool",} 5.0
# HELP thread_pool_queue_size Gauge measuring pending task count in queue
thread_pool_queue_size{pool_name="business_thread_pool",} 23.0
# HELP thread_pool_reject_count Counter tracking rejected task number
thread_pool_reject_count{pool_name="business_thread_pool",} 0.0

6.2 指标字段说明

指标名 含义
thread_pool_core_size 配置核心线程数
thread_pool_max_size 最大线程数
thread_pool_active_threads 当前正在执行任务的活跃线程
thread_pool_queue_size 阻塞队列当前积压任务
thread_pool_queue_remaining 队列剩余空位
thread_pool_completed_task_count 历史已执行完毕任务总数
thread_pool_total_task_count 总共提交任务数
thread_pool_reject_count 被拒绝的任务总数(线程池满了)
相关推荐
暗黑小白1 小时前
脱敏引擎工程化
后端·ai agent
plainGeekDev1 小时前
运行时获取依赖 → 编译时注入
android·java·kotlin
用户8181870627461 小时前
第23章 JPA / Hibernate 异常
后端
用户8181870627461 小时前
第22章 MyBatis / MyBatis-Plus 常见异常与 SQL 调试
后端
雪隐2 小时前
个人电脑玩AI-15让5060 Ti给你打工——MiniMax H3 本地部署实录:一个自带录音棚的视频模型,和它的 NVFP4 瘦身奇遇
前端·人工智能·后端
玖石书3 小时前
ASP.NET Core 迁移至 Spring系列:类库框架篇
java·后端·asp.net
笨蛋不要掉眼泪3 小时前
RabbitMQ消息队列:延迟消息
java·rabbitmq·java-rabbitmq
Adios7943 小时前
搜索二维矩阵 II
java·数据结构·算法
PieroPC3 小时前
Windows 驱动备份与恢复工具 CMD bat
后端