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 被拒绝的任务总数(线程池满了)
相关推荐
卷无止境15 小时前
除了开发api,FastAPI其实也可以配合jinja2模板写页面
后端·python·fastapi
吴声子夜歌15 小时前
Java面试——基础
java·开发语言·面试
newerp15 小时前
Redis 操作与缓存策略
后端·程序员·go
newerp15 小时前
GORM ORM 基础
后端·程序员·go
小岛前端15 小时前
AI Skills 已经封神,但新的问题却越来越严重!
前端·后端·github
拖孩15 小时前
一个人 + AI 做的小程序,上线 15 天赚了 10 块 5
前端·后端·微信小程序
newerp15 小时前
CRUD 操作与预处理语句
后端·程序员·go
智码看视界15 小时前
Day 56:AI辅助开发全面提效:Copilot + Cursor的Java开发
java·单元测试·copilot·cursor·后端开发·代码审查·ai辅助开发
wei_shuo15 小时前
KES 数据同步与ETL实战:数据集成、转换与实时同步方案
后端
乒乓狂魔147867399700015 小时前
LangGraph:用一张图,编排一群 AI 助手
后端