应使用 >= 比较当前时间戳与目标时间戳来判断定时任务是否到达或已过期,而非 ==;需防范系统时间跳变、跨天业务逻辑缺失及高频轮询问题,推荐结合 java.time API 和 ScheduledExecutorService。

用 System.currentTimeMillis() 判断定时任务执行时机,核心是拿当前时间戳跟目标时间戳比大小,但直接硬比容易出错——尤其在跨天、时区、系统时间跳变或任务延迟时。
别只比“是否相等”,要判断“是否到达或已过期”
定时任务通常不是要求“精准到毫秒触发”,而是“在指定时间点或之后尽快执行”。如果写成 if (System.currentTimeMillis() == targetTime),几乎永远不会命中,因为毫秒级精度下极难恰好相等。
- 正确做法是:用
>=判断是否已到或已过目标时间 - 例如:每分钟整点执行,目标时间是
roundDownToMinute(System.currentTimeMillis()) + 60_000,然后循环检查System.currentTimeMillis() >= nextTriggerTime - 一旦满足条件就执行,并立刻计算下一次目标时间,避免重复触发
注意系统时间可能被手动修改或NTP校正
System.currentTimeMillis() 依赖系统时钟,若运维调整了服务器时间(比如回拨1小时),你的定时逻辑可能反复触发或长期不触发。
- 对可靠性要求高的场景,可结合
System.nanoTime()做相对时间估算(但它不反映真实时间,不能替代绝对时间判断) - 更稳妥的方式是引入外部可信时间源(如HTTP请求授时服务),或使用支持时钟漂移补偿的调度框架(如Quartz、XXL-JOB)
- 至少加日志监控:当发现当前时间比上次记录时间倒退超过阈值(如5秒),触发告警并重置状态
避免在循环中高频调用 currentTimeMillis()
虽然这个方法开销很小,但在 tight loop(比如每毫秒轮询)里频繁调用,既没必要,也可能掩盖真正的性能瓶颈或调度问题。
- 合理间隔轮询:比如每100ms查一次,而不是1ms;或者用
Thread.sleep()配合等待,减少CPU空转 - 优先考虑 Java 原生的
ScheduledExecutorService,它内部已优化调度逻辑,不需要你手动轮询 - 如果必须手动控制,建议把下次触发时间存为变量,每次只和它比,而不是每次都重新算目标时间
跨天/节假日等业务规则不能只靠时间戳硬算
纯靠毫秒数无法表达“每周一上午9点”或“每月最后一个工作日”,这类逻辑需要额外解析。
-
System.currentTimeMillis()只提供数字,业务含义(星期几、是否节假日)得自己补全 - 推荐用
java.timeAPI(如LocalDateTime、ZonedDateTime)做语义化计算,再转成毫秒参与判断 - 例如:判断是否到“今天18:00”,不要用固定毫秒偏移,而应每天动态生成当天的
LocalDateTime.of(today, 18, 0).atZone(zone).toInstant().toEpochMilli()
不复杂但容易忽略。

















