Spring Boot多线程定时任务:解决Cron任务阻塞问题
解决Spring Boot定时任务阻塞,实现多线程并行搜索的方案
你这个场景我太熟悉了——Spring默认的定时任务是单线程串行执行的,前一个任务没跑完,后面的就得乖乖排队,100个关键词轮一遍确实要花好几个小时,完全达不到5分钟一批的预期。给你几个落地的方案,一步步搞定:
1. 先给定时任务配置多线程调度池
Spring Boot自带的TaskScheduler默认是单线程的,我们得自定义一个线程池让定时任务能并行启动。
创建一个配置类就行:
@Configuration @EnableScheduling public class SchedulingConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); // 线程池大小根据你的服务器配置来,比如设5个,足够同时跑多批任务 taskScheduler.setPoolSize(5); taskScheduler.setThreadNamePrefix("batch-search-task-"); taskScheduler.initialize(); taskRegistrar.setTaskScheduler(taskScheduler); } }
这样配置后,只要线程池有空闲线程,到点的定时任务就会直接启动,不用等上一批任务跑完,解决了任务排队的问题。
2. 把单个关键词的搜索逻辑异步化
光让定时任务并行还不够,每批10个关键词如果还是串行处理,单批任务耗时还是会很长。我们可以把每个关键词的搜索操作改成异步执行,让同一批里的关键词也能并行处理。
首先开启异步支持,新建一个异步配置类:
@Configuration @EnableAsync public class AsyncSearchConfig { @Bean(name = "keywordSearchExecutor") public Executor asyncSearchExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数设10,刚好对应每批的关键词数量 executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(50); executor.setThreadNamePrefix("single-keyword-search-"); executor.initialize(); return executor; } }
然后修改你的SearchResultService,把单关键词的搜索逻辑改成异步方法:
@Service public class SearchResultService { // 指定用我们刚才配置的异步线程池 @Async("keywordSearchExecutor") public void processSingleKeyword(Keyword keyword) { LOG.debug("开始处理关键词: {}", keyword.getContent()); // 这里放你的网页抓取、结果匹配、入库逻辑 // ... LOG.debug("完成关键词: {}", keyword.getContent()); } public void fetchLatestResults(List<Keyword> keywords) { // 循环把每个关键词丢去异步执行 keywords.forEach(this::processSingleKeyword); } }
这样每批10个关键词会同时在不同线程里处理,单批任务的耗时会大幅缩短,不会再占用定时任务线程太久。
3. 加个状态标记,避免重复处理
这里有个小坑要注意:如果第一批任务的异步逻辑还没跑完,第二批定时任务又启动了,会不会把还在处理的关键词又选走?毕竟你是按“最久未搜索”来选的。
建议给Keyword实体加个search_status字段,比如IDLE(待搜索)、SEARCHING(正在搜索)。然后:
- 在
keywordService.findOldestSearched10()的时候,只筛选search_status = IDLE的关键词 - 选中后立刻把这些关键词的
search_status改成SEARCHING - 等单个关键词处理完成后,再把
search_status改回IDLE,同时更新lastSearchDate
这样就能避免同一关键词被多个任务重复抓取,保证数据的一致性。
内容的提问来源于stack exchange,提问作者Kshitij Bajracharya
相关产品推荐
相关产品推荐

