@Async 线程池踩坑指南
2026/7/23 20:26:27 网站建设 项目流程

在 Spring 项目中,@Async可以让方法异步执行,提高系统吞吐量。但是,如果不注意线程池的配置,很容易踩坑,导致数据库连接耗尽、任务阻塞甚至系统崩溃。本文结合实际案例,分享@Async的坑点、解决方案,以及如何通过消息队列中间件做异步解耦,提高系统稳定性。


1. 问题现象

某项目需要给400 人发送会议通知,每个人触发一个异步通知保存操作。

结果出现了严重问题:

  • 数据库连接池瞬间耗尽
  • 系统报错甚至崩溃
  • 异步任务执行混乱

显然,这并不是单纯的异步问题,而是线程池配置导致的资源抢占。


2. 根本原因

2.1 @Async 默认线程池的坑

Spring 中,如果@Async没有指定线程池,会使用SimpleAsyncTaskExecutor

特点: - 无核心线程,每次调用都创建新线程 - 无队列,任务直接提交到线程执行

也就是说:

400 个通知 = 400 个并发线程同时抢数据库连接 → 连接池瞬间爆满

默认线程池适合轻量异步任务,不适合批量任务或数据库密集型任务。


2.2 为什么项目里全用 flowable-async 线程池

项目中定义了一个@PrimaryapplicationTaskExecutor(flowable-async-*):

@Primary@Bean(name="applicationTaskExecutor")publicThreadPoolTaskExecutorapplicationTaskExecutor(){// core = CPU核数 * 2, max = core * 2}

Spring 默认把所有 @Async 调用都路由到这个池。问题是:

  • 通知任务大量抢占 flowable 的线程
  • flowable 自己的异步任务反而没线程可用

这就造成了业务互相干扰,性能反而下降。


3. 解决方案

3.1 创建专用线程池

为高并发通知任务单独创建线程池:

@Bean(name="noticeTaskExecutor")publicThreadPoolTaskExecutornoticeTaskExecutor(){intcpuCores=Runtime.getRuntime().availableProcessors();intcorePoolSize=Math.min(Math.max(2,cpuCores),3);intmaxPoolSize=Math.min(corePoolSize*2,8);intqueueCapacity=50000;ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();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={}, rejectPolicy=CallerRuns",corePoolSize,maxPoolSize,queueCapacity);}

3.2 指定 @Async 使用专用池

在异步方法上指定线程池
或者把上述链接池配置,改成全局连接池配置

@Async("noticeTaskExecutor")// 指定线程池名称@EventListenerpublicvoidlisten(NoticeRecordSaveEventevent){// 执行通知保存逻辑}

这样:

  • 业务任务不会抢占 flowable 线程
  • 数据库连接池不会瞬间耗尽
  • 异步执行可控、稳定

3.3 更进一步:异步解耦到消息队列中间件

当异步任务量大、数据库或业务资源有限时,可以考虑消息队列解耦

  • 生产者只负责发送消息到队列(如 RabbitMQ、Kafka、RocketMQ)
  • 消费者从队列异步消费,处理通知逻辑
  • 可以自由控制消费并发数、批量处理、重试机制

这样可以达到:

  • 流量削峰:大量任务不会瞬间占用数据库连接
  • 业务解耦:通知逻辑与主业务线程分离
  • 系统稳定:异步任务可按队列速率消费,不会刷爆线程池

结合 Spring,消息队列的消费端也可以结合专用线程池控制并发:

@BeanpublicThreadPoolTaskExecutornoticeConsumerExecutor(){ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();executor.setCorePoolSize(3);executor.setMaxPoolSize(10);executor.setQueueCapacity(2000);executor.setThreadNamePrefix("notice-consumer-");executor.initialize();returnexecutor;}

总结:@Async + 专用线程池适合中小量异步任务,高并发或跨系统异步场景建议结合消息队列做解耦。


4. 总结

原则说明
@Async 必须指定线程池否则用 SimpleAsyncTaskExecutor,无限创建线程
不同业务用不同线程池避免相互影响,通知用专用池
执行方法不加事务事务在同步调用层管理,异步只管执行
线程池大小参考连接池容量corePoolSize 不要超过数据库连接数
高并发异步建议消息队列解耦队列削峰、异步消费、系统更稳定

💡 小结

@Async不是“随便加个注解就行”,合理的线程池设计和资源隔离至关重要。对于高并发异步任务,结合消息队列中间件做异步解耦,可以让系统更稳定、性能更可控。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询