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 分钟前
本地文件搜索工具,双击Ctrl就能呼出搜索
java·服务器·前端·支持向量机·ffmpeg·音视频·模拟退火算法
独隅5 分钟前
Claude 高效使用指南深度评测
java·开发语言·claude·claude code
ByteCoder.13 分钟前
班前十分钟的清单,按体检引导单来写
java·服务器·前端·助播虾
海的透彻15 分钟前
如何让docker容器启动时用自定义的用户
java·docker·dubbo
疯狂学习GIS19 分钟前
将谷歌地球引擎账号升级为Contributor与Partner层级的方法
java·数据库·人工智能
GG.WinGel31 分钟前
质数数目(蓝桥杯练习题)
java·数据结构·算法·青少年编程·蓝桥杯
泡泡糖的中文规格书36 分钟前
HDLbits 2.2 学习笔记 · 多路复用器
java·开发语言
John_Hui38 分钟前
第 3 篇:配置 JDK 17 环境
java·开发语言·ubuntu
秋天的怀念_David43 分钟前
算法一千零一夜 · 第十七夜 长街点灯
java·程序人生·算法
不吃鱼的羊1 小时前
如何应对恐惧
java