




本文介绍如何在 spring boot 中实现高精度定时任务,使 @scheduled 任务严格在每秒的指定毫秒时刻(如 xx:xx:xx.900)触发,摆脱系统启动时间依赖与 cron 秒级限制。
Spring 的 @Scheduled 注解默认支持 cron、fixedDelay 和 fixedRate 等方式,但它们均无法满足「每秒精准偏移 X 毫秒」的需求:
要实现真正与系统时钟对齐的定时行为(例如:20:00:00.900、20:00:01.900、20:00:02.900…),必须自定义 Trigger,并基于 System.currentTimeMillis() 或 Instant.now() 动态计算下一个绝对目标时间戳。
以下是一个生产就绪的轻量级实现,不依赖前次执行时间,而是始终对齐到「最近的整秒 + 指定毫秒」:
public class AlignedSecondTrigger implements Trigger {
private final int offsetMs; // 如 900,表示每秒第 900 毫秒触发
public AlignedSecondTrigger(int offsetMs) {
if (offsetMs < 0 || offsetMs >= 1000) {
throw new IllegalArgumentException("offsetMs must be in [0, 999]");
}
this.offsetMs = offsetMs;
}
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
long now = System.currentTimeMillis();
// 计算当前时间所属的“整秒起点”(向下取整到秒)
long secondStart = now - (now % 1000);
// 目标时间 = 整秒起点 + offsetMs;若已过该时刻,则取下一秒
long target = secondStart + offsetMs;
if (target <= now) {
target += 1000; // 推迟到下一秒的对应毫秒点
}
return new Date(target);
}
}? 关键设计说明: 使用 System.currentTimeMillis() 获取当前绝对时间; 通过 now % 1000 剥离毫秒部分,得到本秒起始时间戳; 加上 offsetMs 得到目标时间;若该时间已过去(即当前毫秒 > offsetMs),则自动+1000ms跳至下一秒——确保严格按节奏推进,无累积误差。
@Configuration
@EnableScheduling
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar registrar) {
// 每秒在 .900 时刻执行
registrar.addTriggerTask(
() -> System.out.println("Fired at: " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss.SSS"))),
new AlignedSecondTrigger(900)
);
}
}
通过实现 Trigger 接口并基于绝对时间动态计算下次执行点,可突破 Spring @Scheduled 的固有局限,实现稳定、可预测的毫秒级定时对齐。该方案简洁、无外部依赖、易于测试与维护,是 Spring Boot 应用中实现「每秒准点触发」的推荐实践。