SpringBoot监控所有线程池的四种解决方案及代码案例

 更新时间:2025年12月01日 10:00:03   作者:学亮编程手记  
这篇文章介绍了四种监控Spring Boot中所有线程池的解决方案,并提供了代码案例,推荐使用自动发现机制来监控所有线程池,需要的朋友可以参考下

问题分析

1.默认监控的局限性

@Component
public class ThreadPoolMonitor {
    
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor; // 只能监控这一个线程池
    
    public void monitor() {
        // 只能监控 taskExecutor 这个特定的bean
        System.out.println("活跃线程: " + taskExecutor.getActiveCount());
    }
}

解决方案

方案1:手动注册所有线程池

@Component
public class ThreadPoolMonitor {
    
    private final Map<String, ThreadPoolTaskExecutor> executors = new ConcurrentHashMap<>();
    
    // 手动注册线程池
    public void registerExecutor(String name, ThreadPoolTaskExecutor executor) {
        executors.put(name, executor);
    }
    
    @Scheduled(fixedRate = 30000)
    public void monitorAll() {
        executors.forEach((name, executor) -> {
            ThreadPoolExecutor pool = executor.getThreadPoolExecutor();
            log.info("线程池[{}] - 活跃: {}/{}, 队列: {}/{}, 完成: {}",
                name,
                pool.getActiveCount(),
                pool.getPoolSize(),
                pool.getQueue().size(),
                pool.getQueue().remainingCapacity() + pool.getQueue().size(),
                pool.getCompletedTaskCount());
        });
    }
}

// 在配置中注册
@Configuration
public class ExecutorConfig {
    
    @Autowired
    private ThreadPoolMonitor monitor;
    
    @Bean("emailExecutor")
    public ThreadPoolTaskExecutor emailExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 配置...
        executor.initialize();
        
        // 注册到监控器
        monitor.registerExecutor("emailExecutor", executor);
        return executor;
    }
    
    @Bean("smsExecutor")
    public ThreadPoolTaskExecutor smsExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 配置...
        executor.initialize();
        
        monitor.registerExecutor("smsExecutor", executor);
        return executor;
    }
}

方案2:自动发现所有线程池(推荐)

@Component
public class GlobalThreadPoolMonitor {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Scheduled(fixedRate = 30000)
    public void monitorAllThreadPools() {
        // 获取所有 ThreadPoolTaskExecutor 类型的bean
        Map<String, ThreadPoolTaskExecutor> executors = 
            applicationContext.getBeansOfType(ThreadPoolTaskExecutor.class);
        
        // 获取所有 ThreadPoolExecutor 类型的bean(直接创建的)
        Map<String, ThreadPoolExecutor> nativeExecutors = 
            applicationContext.getBeansOfType(ThreadPoolExecutor.class);
        
        log.info("=== 线程池监控报告 ===");
        
        // 监控 Spring 封装的线程池
        executors.forEach((beanName, executor) -> {
            if (executor.getThreadPoolExecutor() != null) {
                printPoolStats(beanName, executor.getThreadPoolExecutor());
            }
        });
        
        // 监控原生线程池
        nativeExecutors.forEach((beanName, executor) -> {
            printPoolStats(beanName, executor);
        });
    }
    
    private void printPoolStats(String name, ThreadPoolExecutor executor) {
        log.info("线程池[{}]: 活跃{}/核心{}, 队列{}/{}, 完成任务: {}, 拒绝: {}",
            name,
            executor.getActiveCount(),
            executor.getPoolSize(),
            executor.getQueue().size(),
            executor.getQueue().size() + executor.getQueue().remainingCapacity(),
            executor.getCompletedTaskCount(),
            executor.getRejectedExecutionHandler().getClass().getSimpleName());
    }
}

方案3:监控 @Async 使用的线程池

@Component
public class AsyncThreadPoolMonitor {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    @Scheduled(fixedRate = 30000)
    public void monitorAsyncPools() {
        try {
            // 通过反射获取Spring内部的线程池
            Map<String, Executor> asyncExecutors = 
                applicationContext.getBeansOfType(Executor.class);
            
            asyncExecutors.forEach((name, executor) -> {
                if (executor instanceof ThreadPoolTaskExecutor) {
                    ThreadPoolExecutor pool = ((ThreadPoolTaskExecutor) executor).getThreadPoolExecutor();
                    printAsyncPoolStats(name, pool);
                } else if (executor instanceof ThreadPoolExecutor) {
                    printAsyncPoolStats(name, (ThreadPoolExecutor) executor);
                } else if (executor instanceof TaskExecutor) {
                    log.info("Executor [{}]: 类型 {}", name, executor.getClass().getSimpleName());
                }
            });
        } catch (Exception e) {
            log.warn("监控异步线程池失败: {}", e.getMessage());
        }
    }
    
    private void printAsyncPoolStats(String name, ThreadPoolExecutor pool) {
        double usageRate = pool.getMaximumPoolSize() > 0 ? 
            (double) pool.getActiveCount() / pool.getMaximumPoolSize() * 100 : 0;
            
        log.warn("异步线程池[{}]: 活跃{}/最大{}, 使用率: {:.1f}%, 队列: {}/{}",
            name,
            pool.getActiveCount(),
            pool.getMaximumPoolSize(),
            usageRate,
            pool.getQueue().size(),
            pool.getQueue().size() + pool.getQueue().remainingCapacity());
    }
}

方案4:集成Micrometer监控(生产环境推荐)

@Component
public class MicrometerThreadPoolMonitor {
    
    private final MeterRegistry meterRegistry;
    private final List<ThreadPoolExecutor> monitoredPools = new ArrayList<>();
    
    public MicrometerThreadPoolMonitor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    // 注册要监控的线程池
    public void registerPool(String name, ThreadPoolExecutor pool) {
        monitoredPools.add(pool);
        
        // 注册指标
        Gauge.builder("thread.pool.active.count", pool, ThreadPoolExecutor::getActiveCount)
            .tag("pool", name)
            .description("活跃线程数")
            .register(meterRegistry);
            
        Gauge.builder("thread.pool.queue.size", pool, p -> p.getQueue().size())
            .tag("pool", name)
            .description("队列大小")
            .register(meterRegistry);
            
        Gauge.builder("thread.pool.completed.tasks", pool, ThreadPoolExecutor::getCompletedTaskCount)
            .tag("pool", name)
            .description("完成任务数")
            .register(meterRegistry);
    }
    
    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) {
        // 应用启动后自动发现并注册所有线程池
        ApplicationContext context = event.getApplicationContext();
        
        Map<String, ThreadPoolTaskExecutor> springExecutors = 
            context.getBeansOfType(ThreadPoolTaskExecutor.class);
        
        Map<String, ThreadPoolExecutor> nativeExecutors = 
            context.getBeansOfType(ThreadPoolExecutor.class);
        
        springExecutors.forEach((name, executor) -> {
            if (executor.getThreadPoolExecutor() != null) {
                registerPool("spring-" + name, executor.getThreadPoolExecutor());
            }
        });
        
        nativeExecutors.forEach((name, executor) -> {
            registerPool("native-" + name, executor);
        });
        
        log.info("已注册监控的线程池数量: {}", monitoredPools.size());
    }
}

完整的生产级监控方案

@Configuration
public class ThreadPoolMonitorConfig {
    
    @Bean
    @ConditionalOnMissingBean
    public GlobalThreadPoolMonitor globalThreadPoolMonitor() {
        return new GlobalThreadPoolMonitor();
    }
}

@Component
@Slf4j
public class GlobalThreadPoolMonitor {
    
    @Autowired
    private ApplicationContext applicationContext;
    
    private final Map<String, ThreadPoolExecutor> allPools = new ConcurrentHashMap<>();
    
    @PostConstruct
    public void init() {
        discoverAllThreadPools();
    }
    
    @Scheduled(fixedRate = 30000)
    public void monitorAllPools() {
        if (allPools.isEmpty()) {
            discoverAllThreadPools();
        }
        
        log.info("======= 线程池监控报告 =======");
        allPools.forEach(this::logPoolStatus);
        log.info("======= 监控报告结束 =======");
    }
    
    private void discoverAllThreadPools() {
        // 发现Spring封装的线程池
        applicationContext.getBeansOfType(ThreadPoolTaskExecutor.class)
            .forEach((name, executor) -> {
                if (executor.getThreadPoolExecutor() != null) {
                    allPools.put("Spring-" + name, executor.getThreadPoolExecutor());
                }
            });
        
        // 发现原生线程池
        applicationContext.getBeansOfType(ThreadPoolExecutor.class)
            .forEach((name, executor) -> {
                allPools.put("Native-" + name, executor);
            });
        
        // 发现所有Executor(包括@Async使用的)
        applicationContext.getBeansOfType(Executor.class)
            .forEach((name, executor) -> {
                if (executor instanceof ThreadPoolTaskExecutor) {
                    ThreadPoolExecutor pool = ((ThreadPoolTaskExecutor) executor).getThreadPoolExecutor();
                    allPools.putIfAbsent("Executor-" + name, pool);
                } else if (executor instanceof ThreadPoolExecutor) {
                    allPools.putIfAbsent("Executor-" + name, (ThreadPoolExecutor) executor);
                }
            });
        
        log.info("发现线程池数量: {}", allPools.size());
    }
    
    private void logPoolStatus(String name, ThreadPoolExecutor pool) {
        int activeCount = pool.getActiveCount();
        int poolSize = pool.getPoolSize();
        int queueSize = pool.getQueue().size();
        int queueCapacity = queueSize + pool.getQueue().remainingCapacity();
        long completedTasks = pool.getCompletedTaskCount();
        
        String status = (activeCount == 0) ? "空闲" : "忙碌";
        double usageRate = pool.getMaximumPoolSize() > 0 ? 
            (double) activeCount / pool.getMaximumPoolSize() * 100 : 0;
            
        if (usageRate > 80) {
            log.warn("🚨 线程池[{}]: {} 活跃{}/最大{} (使用率{:.1f}%), 队列{}/{}", 
                name, status, activeCount, pool.getMaximumPoolSize(), usageRate, 
                queueSize, queueCapacity);
        } else {
            log.info("线程池[{}]: {} 活跃{}/核心{}, 队列{}/{}, 完成: {}", 
                name, status, activeCount, poolSize, queueSize, queueCapacity, completedTasks);
        }
    }
    
    // 获取特定线程池状态
    public ThreadPoolStats getPoolStats(String poolName) {
        ThreadPoolExecutor pool = allPools.get(poolName);
        if (pool != null) {
            return new ThreadPoolStats(
                pool.getActiveCount(),
                pool.getPoolSize(),
                pool.getQueue().size(),
                pool.getQueue().remainingCapacity(),
                pool.getCompletedTaskCount()
            );
        }
        return null;
    }
    
    // 统计类
    @Data
    @AllArgsConstructor
    public static class ThreadPoolStats {
        private int activeCount;
        private int poolSize;
        private int queueSize;
        private int queueRemainingCapacity;
        private long completedTaskCount;
    }
}

总结

  • 默认情况下,ThreadPoolMonitor 只能监控直接注入的特定线程池
  • 需要特殊处理才能监控所有线程池:
    • 自动发现所有 ThreadPoolTaskExecutorThreadPoolExecutor bean
    • 注册机制手动管理
    • 集成监控框架如 Micrometer
  • 生产推荐:使用方案2或方案4的自动发现机制

关键是要在应用启动后自动发现所有线程池实例,而不是依赖单个注入。

到此这篇关于SpringBoot监控所有线程池的四种解决方案及代码案例的文章就介绍到这了,更多相关SpringBoot监控所有线程池内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • springboot Validated失效的问题及解决思路

    springboot Validated失效的问题及解决思路

    文章主要介绍了Java Bean Validation(JSR 303/JSR 349)和Hibernate Validator的基本用法,包括常用注解的使用、@Valid和@Validated注解的区别、如何自定义校验注解以及如何在Spring Boot中使用这些校验机制
    2026-01-01
  • 阿里nacos+springboot+dubbo2.7.3统一处理异常的两种方式

    阿里nacos+springboot+dubbo2.7.3统一处理异常的两种方式

    本文主要介绍了阿里nacos+springboot+dubbo2.7.3统一处理异常的两种方式,文中根据实例编码详细介绍的十分详尽,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • springboot 运行 jar 包读取外部配置文件的问题

    springboot 运行 jar 包读取外部配置文件的问题

    这篇文章主要介绍了springboot 运行 jar 包读取外部配置文件,本文主要描述linux系统执行jar包读取jar包同级目录的外部配置文件,主要分为两种方法,每种方法通过实例代码介绍的非常详细,需要的朋友可以参考下
    2021-07-07
  • Java实现的简单数字处理类及用法示例

    Java实现的简单数字处理类及用法示例

    这篇文章主要介绍了Java实现的简单数字处理类及用法,涉及java数字运算相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Java集合Map常见问题_动力节点Java学院整理

    Java集合Map常见问题_动力节点Java学院整理

    这篇文章主要为大家详细整理了Java集合Map常见问题,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • SpringBoot自定义Redis实现缓存序列化详解

    SpringBoot自定义Redis实现缓存序列化详解

    Spring提供了一个RedisTemplate来进行对Redis的操作,但是RedisTemplate默认配置的是使用Java本机序列化。如果要对对象操作,就不是那么的方便。所以本文为大家介绍了另一种SpringBoot结合Redis实现序列化的方法,需要的可以参考一下
    2022-07-07
  • mybatis如何批量更新list对象

    mybatis如何批量更新list对象

    这篇文章主要介绍了mybatis如何批量更新list对象问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 使用springboot不自动初始化数据库连接池

    使用springboot不自动初始化数据库连接池

    这篇文章主要介绍了使用springboot不自动初始化数据库连接池,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 一文详解MyBatis中动态SQL的封装原理与常用标签实战应用

    一文详解MyBatis中动态SQL的封装原理与常用标签实战应用

    这篇文章主要为大家详细介绍了MyBatis中动态SQL的核心技术与及常用标签的应用,同时提供了最佳实践和常见问题解决方案,感兴趣的小伙伴可以跟随小编一起学习一下
    2026-04-04
  • 彻底解决tomcat中文乱码问题方案

    彻底解决tomcat中文乱码问题方案

    这篇文章主要介绍了彻底解决tomcat中文乱码问题方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11

最新评论