Springboot 自定义线程池 ThreadPoolTaskExecutor

场景

假设 需要使用多线程清理es中的历史数据

知识

参数解释

 
  1. corePoolSize(核心线程数):线程池中的核心线程数量,即使线程池处于空闲状态,这些核心线程也不会被销毁。
  2. maximumPoolSize(最大线程数):线程池允许创建的最大线程数量。如果阻塞队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。
  3. keepAliveTime(非核心线程的空闲时间):非核心线程等待keepAliveTime时间后还没有获取到任务就会自动销毁。
  4. unit(空闲时间单位)keepAliveTime的时间单位。
  5. workQueue(任务队列):用于保存等待执行的任务的阻塞队列。
  6. ThreadFactory(线程工厂):用于创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字,后期方便定位问题。
  7. RejectedExecutionHandler(拒绝策略):当线程池中的线程达到maximumPoolSize,说明线程池处于饱和状态,此时仍然有任务提交过来,那么必须采取一种策略处理提交的新任务。

拒绝策略:当线程池中的线程达到最大数量,且任务队列已满时,需要采取一种策略来处理新提交的任务。ThreadPoolTaskExecutor提供了以下几种拒绝策略:

 
  1. AbortPolicy(默认拒绝策略):当实际线程数量大于维护队列中设定的数量时,将会触发拒绝任务的处理程序,它将抛出RejectedExecutionException
  2. DiscardPolicy:当实际线程数量大于维护队列中设定的数量时,新提交的任务将被静默丢弃。
  3. DiscardOldestPolicy:当实际线程数量大于维护队列中设定的数量时,队列中最老的任务将被静默丢弃,新任务将被放入队列。
  4. CallerRunsPolicy:当实际线程数量大于维护队列中设定的数量时,多出来的任务将由调用线程(主线程)处理。

实现方案

1.一个线程安全(保证时间段连贯)的参数生产方法(产生es 清理所需的时间段,索引,时间字段),定义一个外配置的json文件去做持久化记录时间段的位置(根据个人需要,完全可以摈弃)

2.一个es 清理的方法,我们用es的_delete_by_query 去处理

3.一个自定义线程池elasticsearchClearPool,定时任务去调用es 的清理方法 定时任务的线程为es-clear-main 清理的主线程,elasticsearchClearPool为es数据的清理的线程,

线程池的拒绝策略设置为:ThreadPoolExecutor.CallerRunsPolicy() (使用该策略保证清理时间的连贯性)

4.一个定时任务定时启动清理方法 使用fixedDelay  保证单一性

代码

只贴部分与文章相关的代码(存在冗余,使用按需改动即可)

自定义线程池配置


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concur
### 如何在Spring Boot中实现自定义线程池的优雅关闭 为了确保应用程序能够正常处理请求并安全地终止正在运行的任务,在Spring Boot应用中实现自定义线程池时,应当考虑其优雅关闭机制。这不仅有助于防止数据丢失或损坏,还能提升系统的稳定性和可靠性。 #### 定义Bean以创建自定义线程池 首先,需通过`@Configuration`类来声明一个或多个用于构建不同用途线程池实例的方法,并将其注册为Spring容器中的bean对象: ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ThreadPoolConfig { @Bean(name = "customTaskExecutor") public TaskExecutor customTaskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置基本属性 executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); // 初始化前调用此方法完成其他设置 executor.initialize(); return executor; } } ``` 上述代码片段展示了如何基于默认配置初始化一个新的`ThreadPoolTaskExecutor`实例[^1]。 #### 注册Shutdown Hook以便于优雅退出 为了让程序能够在接收到停止信号后有序结束所有活动任务而不影响服务的整体可用性,可以在启动阶段向JVM注册一个shutdown hook。当进程准备终止时,该hook会被触发从而允许我们执行必要的清理工作: ```java import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class GracefulShutdownHook { private static final Logger logger = LoggerFactory.getLogger(GracefulShutdownHook.class); @Autowired private ThreadPoolTaskExecutor taskExecutor; @PostConstruct public void init() { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { logger.info("Starting graceful shutdown..."); // 停止接收新任务并将现有任务排队等待完成 taskExecutor.shutdown(); // 等待一段时间让已提交的任务有机会被执行完毕 if (!taskExecutor.awaitTermination(30, TimeUnit.SECONDS)) { logger.warn("Not all tasks have completed execution after the timeout."); // 如果超时则强制中断剩余未完成的工作项 List<Runnable> droppedTasks = taskExecutor.getRejectedExecutionHandler() .getUnprocessedTasks(taskExecutor); logger.error("{} tasks did not complete before termination.", droppedTasks.size()); } logger.info("Application has been shut down gracefully."); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // Restore interrupted status logger.error("Interrupted during graceful shutdown", e); } })); } @PreDestroy public void destroy() throws InterruptedException { logger.debug("Executing PreDestroy method"); } } ``` 这段逻辑实现了对指定线程池组件的安全销毁过程管理。 #### 处理异常情况下的快速失败模式 除了正常的停机流程外,还应该考虑到某些情况下可能无法给予足够的时间去等待所有的后台作业都顺利完成的情形。此时可以通过调整线程池的相关参数(如最大存活时间和队列容量),以及采用合适的拒绝策略(例如AbortPolicy)来尽早发现潜在的问题并采取相应的措施加以应对[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值