Spring Boot多线程调度:如何每秒启动新线程直至线程池耗尽?
解决Spring Boot每秒启动新线程直到线程池耗尽的问题
原配置的问题分析
你之前的配置里,<task:scheduled>是让调度器直接执行doSomething方法,Spring的调度逻辑默认对同一个任务不会并发执行——哪怕你给调度器设置了pool-size="5",它也只会用单个线程来处理这个任务的调度流程,上一次任务没执行完,下一次调度就会等待,所以不管用fixed-delay还是fixed-rate,都没法实现每秒启动新线程的效果。
解决方案:用调度器提交任务到线程池
核心思路是:让调度器只负责每秒触发一次任务提交动作,真正的任务执行交给独立的线程池,这样就能实现每秒启动一个新线程,直到线程池被耗尽。
1. XML配置方式
第一步:配置线程池和调度器
<!-- 配置任务执行线程池,核心/最大线程数为5,队列容量0(线程池满时直接拒绝新任务) --> <task:executor id="myExecutor" pool-size="5" queue-capacity="0" rejection-policy="ABORT" /> <!-- 调度器只需要1个线程,负责每秒提交任务 --> <task:scheduler id="myScheduler" pool-size="1" /> <!-- 定义任务提交器,用来把任务提交到线程池 --> <bean id="taskSubmitter" class="com.example.TaskSubmitter"> <property name="executor" ref="myExecutor" /> <property name="taskBean" ref="someBean" /> </bean> <!-- 调度任务提交器的submitTask方法,每秒执行一次 --> <task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="taskSubmitter" method="submitTask" fixed-rate="1000"/> </task:scheduled-tasks>
第二步:实现TaskSubmitter类
public class TaskSubmitter { private TaskExecutor executor; private SomeBean taskBean; // 每秒被调度器调用,提交任务到线程池 public void submitTask() { try { executor.submit(() -> taskBean.doSomething()); } catch (RejectedExecutionException e) { // 线程池满时的处理逻辑,比如打日志 System.err.println("线程池已耗尽,无法提交新任务"); } } // 提供setter方法让Spring注入依赖 public void setExecutor(TaskExecutor executor) { this.executor = executor; } public void setTaskBean(SomeBean taskBean) { this.taskBean = taskBean; } }
2. 注解配置方式
第一步:开启调度和异步支持
在Spring Boot启动类上添加注解:
@SpringBootApplication @EnableScheduling // 开启任务调度 @EnableAsync // 开启异步任务支持 public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
第二步:配置异步线程池
@Configuration public class AsyncThreadPoolConfig { @Bean("taskExecutor") public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); // 核心线程数 executor.setMaxPoolSize(5); // 最大线程数 executor.setQueueCapacity(0); // 队列容量设为0,线程池满时直接拒绝新任务 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略 executor.setThreadNamePrefix("Task-Thread-"); // 线程名前缀,方便排查 executor.initialize(); return executor; } }
第三步:实现调度和任务提交
有两种方式:
方式一:@Scheduled + @Async 结合
直接给任务方法添加@Async和@Scheduled,调度器每秒触发一次,任务会被提交到异步线程池:
@Component public class SomeBean { @Async("taskExecutor") // 指定用上面配置的线程池 @Scheduled(fixedRate = 1000) // 每秒触发一次 public void doSomething() { // 你的任务逻辑,比如模拟长时间执行 try { Thread.sleep(10000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("任务执行线程:" + Thread.currentThread().getName()); } }
方式二:手动调度提交
如果需要更灵活的控制(比如处理线程池满的情况),可以单独写调度方法:
@Component public class TaskSubmitScheduler { @Autowired @Qualifier("taskExecutor") private TaskExecutor executor; @Autowired private SomeBean someBean; @Scheduled(fixedRate = 1000) public void submitTask() { try { executor.submit(() -> someBean.doSomething()); } catch (RejectedExecutionException e) { System.err.println("线程池已耗尽,无法提交新任务"); } } }
关键说明
- 把
queue-capacity设为0是为了让线程池满时直接拒绝新任务,符合你“直到线程池被耗尽”的需求;如果保留默认无界队列,线程池满后任务会堆积在队列里,不会停止启动新线程。 - 可以根据需求调整
rejection-policy:比如DISCARD会直接丢弃任务,CALLER_RUNS会让提交任务的线程(调度器线程)自己执行任务。
内容的提问来源于stack exchange,提问作者jan
相关产品推荐
相关产品推荐

