前言
最近有很多同学和我交流说在生产环境中使用原生的线程池会出现一些不可避免的上下文切换开销过大或者任务调度不均衡的情况。
其实在java的java.util.concurrent包中已经高度封装了一个专门给定时任务使用的多线程池类ThreadPoolTaskScheduler。
它能根据具体的定时任务自动回收资源,以免造成线程泄露等情况。我们一起看看。
ThreadPoolTaskScheduler
这个类允许你以与 Timer 类似的方式安排 Runnable 或 callable 任务在给定的延迟后运行,或者定期执行。
基本介绍
线程池
ThreadPoolTaskScheduler 使用一个线程池来执行任务。这意味着它可以在多个任务之间共享一组线程,从而提高执行效率。
任务提交
你可以使用 schedule 方法来安排任务的执行。这个方法接受一个 Runnable 或 callable 对象,以及一个时间参数(表示延迟时间)。
重复执行
scheduleAtFixedRate
或 scheduleWithFixedDelay 方法来安排任务以固定的频率或延迟重复执行。
取消任务
每个安排的任务都有一个与之关联的 Future 对象,你可以使用这个对象来取消任务。
线程池参数
你可以通过 ThreadPoolTaskScheduler 的构造函数来定制线程池的行为,例如设置线程池的核心、最大和最小线程数,以及线程的存活时间等。
与定时任务 Timer 的对比
ThreadPoolTaskScheduler
相比 java.util.Timer 的优势在于它使用了线程池,因此可以更有效地利用系统资源。此外,ThreadPoolTaskScheduler 支持更复杂的调度需求,例如可重复和可取消的任务。
定时场景下与其他线程池对比好在哪里
线程管理
通过设置线程池大小、队列容量等参数,可以控制并发任务的数量以及处理任务请求的方式,避免过多线程造成的资源消耗。
任务调度
能够按照cron表达式或者其他调度策略定期或周期性地执行任务,同时支持一次性任务的执行。
线程命名前缀
可以自定义线程名称前缀,便于在日志和监控工具中识别和追踪各个线程的工作情况。
异常处理
提供了回调机制来处理任务执行过程中抛出的异常。
动态调整
在运行时可以根据需要动态调整线程池的大小和其他相关参数。
松耦合代码架构实现
下面是我自己总结的一个常用模板
public class SchedulerConfig implements SchedulingConfigurer {
private CouponRepository couponRepository;
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(4); // 根据实际资源情况设置线程数
scheduler.initialize();
taskRegistrar.setTaskScheduler(scheduler);
taskRegistrar.addCronTask(() -> {
int batchSize = 1000;
while (true) {
List<Coupon> couponsToDelete = couponRepository.findExpiredCoupons(batchSize);
if (couponsToDelete.isEmpty()) {
break; // 如果没有更多数据,则退出循环
}
try {
// 假设你的CouponRepository有一个支持批量删除的方法
couponRepository.batchDelete(couponsToDelete);
} catch (Exception e) {
// 异常处理与日志记录
log.error("Error occurred during batch deletion", e);
}
}
}, "0 1 * * *"); // 每天凌晨1点开始执行
}
}
原文始发于微信公众号(代码小铺):Java定时任务把CPU资源都吃完了?看看如何优雅的写多线程定时任务吧!
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论