@Async 线程池踩坑指南

发布时间:2026/7/23 20:26:41

@Async 线程池踩坑指南 在 Spring 项目中Async可以让方法异步执行提高系统吞吐量。但是如果不注意线程池的配置很容易踩坑导致数据库连接耗尽、任务阻塞甚至系统崩溃。本文结合实际案例分享Async的坑点、解决方案以及如何通过消息队列中间件做异步解耦提高系统稳定性。1. 问题现象某项目需要给400 人发送会议通知每个人触发一个异步通知保存操作。结果出现了严重问题数据库连接池瞬间耗尽系统报错甚至崩溃异步任务执行混乱显然这并不是单纯的异步问题而是线程池配置导致的资源抢占。2. 根本原因2.1 Async 默认线程池的坑Spring 中如果Async没有指定线程池会使用SimpleAsyncTaskExecutor特点 - 无核心线程每次调用都创建新线程 - 无队列任务直接提交到线程执行也就是说400 个通知 400 个并发线程同时抢数据库连接 → 连接池瞬间爆满默认线程池适合轻量异步任务不适合批量任务或数据库密集型任务。2.2 为什么项目里全用 flowable-async 线程池项目中定义了一个Primary的applicationTaskExecutorflowable-async-*PrimaryBean(nameapplicationTaskExecutor)publicThreadPoolTaskExecutorapplicationTaskExecutor(){// core CPU核数 * 2, max core * 2}Spring 默认把所有 Async 调用都路由到这个池。问题是通知任务大量抢占 flowable 的线程flowable 自己的异步任务反而没线程可用这就造成了业务互相干扰性能反而下降。3. 解决方案3.1 创建专用线程池为高并发通知任务单独创建线程池Bean(namenoticeTaskExecutor)publicThreadPoolTaskExecutornoticeTaskExecutor(){intcpuCoresRuntime.getRuntime().availableProcessors();intcorePoolSizeMath.min(Math.max(2,cpuCores),3);intmaxPoolSizeMath.min(corePoolSize*2,8);intqueueCapacity50000;ThreadPoolTaskExecutorexecutornewThreadPoolTaskExecutor();executor.setCorePoolSize(corePoolSize);executor.setMaxPoolSize(maxPoolSize);executor.setQueueCapacity(queueCapacity);executor.setThreadNamePrefix(async-event-);executor.setWaitForTasksToCompleteOnShutdown(true);executor.setRejectedExecutionHandler(newThreadPoolExecutor.CallerRunsPolicy());executor.initialize();log.info(Async DB TaskExecutor initialized: core{}, max{}, queue{}, rejectPolicyCallerRuns,corePoolSize,maxPoolSize,queueCapacity);}3.2 指定 Async 使用专用池在异步方法上指定线程池或者把上述链接池配置改成全局连接池配置Async(noticeTaskExecutor)// 指定线程池名称EventListenerpublicvoidlisten(NoticeRecordSaveEventevent){// 执行通知保存逻辑}这样业务任务不会抢占 flowable 线程数据库连接池不会瞬间耗尽异步执行可控、稳定3.3 更进一步异步解耦到消息队列中间件当异步任务量大、数据库或业务资源有限时可以考虑消息队列解耦生产者只负责发送消息到队列如 RabbitMQ、Kafka、RocketMQ消费者从队列异步消费处理通知逻辑可以自由控制消费并发数、批量处理、重试机制这样可以达到流量削峰大量任务不会瞬间占用数据库连接业务解耦通知逻辑与主业务线程分离系统稳定异步任务可按队列速率消费不会刷爆线程池结合 Spring消息队列的消费端也可以结合专用线程池控制并发BeanpublicThreadPoolTaskExecutornoticeConsumerExecutor(){ThreadPoolTaskExecutorexecutornewThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.setMaxPoolSize(10);executor.setQueueCapacity(2000);executor.setThreadNamePrefix(notice-consumer-);executor.initialize();returnexecutor;}总结Async 专用线程池适合中小量异步任务高并发或跨系统异步场景建议结合消息队列做解耦。4. 总结原则说明Async 必须指定线程池否则用 SimpleAsyncTaskExecutor无限创建线程不同业务用不同线程池避免相互影响通知用专用池执行方法不加事务事务在同步调用层管理异步只管执行线程池大小参考连接池容量corePoolSize 不要超过数据库连接数高并发异步建议消息队列解耦队列削峰、异步消费、系统更稳定 小结Async不是“随便加个注解就行”合理的线程池设计和资源隔离至关重要。对于高并发异步任务结合消息队列中间件做异步解耦可以让系统更稳定、性能更可控。

相关新闻