情况:
我使用的是注解@Scheduled设置的定时任务,每五分钟执行一次,但是项目却有时候会同一时刻执行多次
还发出了黄色警告
2023-10-30T19:32:19.874+08:00 WARN 7960 — [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=1h7m20s50ms583µs900ns).
数据库中也同时插入多条数据
原因:
在查询资料之后发现,上面的警告说的就是由Hikari连接池的线程饥饿或时钟跳跃引起的。这可能是因为连接池中的线程长时间处于繁忙状态,无法满足并发请求,或者系统时钟在某个时间点发生了跳跃。
再经过思考以及对比之后,我发现这种情况是出现再项目运行过程中,忽然断网之后,再次连接网络时出现的
再次经过思考之后,出现这种情况的原因应该是因为: 在断网之后,启动了第一个定时任务(启动了一个线程),在第一个任务中,向远程发起了请求,但是因为断网,一直得不到回应,任务一直无法结束,之后的任务依旧按时执行,但是同样无法结束,就这样一直堆积,在联网之后就一起爆发出来了
进一步探索:断网导致的任务堆积
可以看出,是从18:24之后就断网了,在19:32恢复网络,之后就开始爆发式执行任务,在查询资料之后发现,这是断网导致的任务堆积
同时堆积的任务个数*五分钟,刚好等于断网时间,进一步证明了这个
解决:
思路很简单,只需要在执行定时任务之前,检查是否联网,未联网则直接结束定时任务,避免定时任务堆积
@Async
@Scheduled(fixedRateString = "3000", initialDelay = 0)
public void executeSaveClimateData(){
if(isNetworkConnected()){
//定时任务逻辑
} else {
log.warn("网络连接异常");
}
}
private boolean isNetworkConnected() {
// 检测网络连接状态的逻辑
try {
// 尝试连接一个公共的DNS服务器(如谷歌)
InetAddress.getByName("8.8.8.8").isReachable(1000);
return true;
} catch (IOException e) {
return false;
}
}
为什么会出现断网导致任务堆积?
Spring Boot默认使用的调度框架是Spring Framework中的Task Scheduling(任务调度)功能。这个功能基于Java的ScheduledExecutorService实现,并提供了一些便捷的注解和配置来进行任务调度。
在默认情况下,当任务由@Scheduled
注解标记并配置了适当的定时表达式时,Spring会创建一个线程池来执行这些任务。如果任务无法按计划执行(例如因为断网),Spring不会对其进行特殊处理。
这意味着,如果应用程序发生了网络中断,然后网络恢复,那么未执行的定时任务将被放置在队列中等待执行。当网络恢复后,任务将立即开始执行。如果有多个任务同时触发,它们可能会并发地执行,并在短时间内产生任务堆积。