秒级精准定时任务可通过预生成时间点+双阶段等待实现:启动时解析Cron为当日有序毫秒列表,主循环休眠至下一时刻后对齐秒边界执行;跨天自动重建,多表达式合并入TreeSet统一调度。

不依赖调度框架,仅用基础循环实现秒级精准定时任务,关键在于把“解析 Cron”和“等待执行”拆开做:先静态生成所有合法时间点,再用轻量循环驱动执行。复杂表达式不必实时解析,而是预计算、缓存、滚动更新。
预生成全天时间点列表,避免运行时反复解析
对固定 Cron 表达式(如 8,28,48 * 7-23 * * ? *),直接在启动时解析一次,生成当天所有目标时刻(LocalDateTime 或 long 毫秒值),按升序排列。例如该表达式表示:每天 7–23 点之间,每小时的第 8、28、48 秒,共 17 小时 × 3 = 51 个时间点。不用写通用 Cron 解析器,只需针对已知格式硬编码解析逻辑——读取分钟字段的 8,28,48,时字段的 7-23,组合出完整秒级时间戳列表。
- 用 LocalDate.now() 获取当天日期,配合 LocalTime.of(hour, minute, second) 构造 LocalDateTime
- 转为 Instant.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() 得到毫秒值,便于后续比较
- 列表只存当天时间点;午夜前 1 分钟检查是否需刷新下一日列表
主循环采用“休眠 + 对齐”双阶段等待策略
纯 while(true) 轮询会空耗 CPU。正确做法是:每次循环计算距下一个时间点的毫秒差,调用 Thread.sleep() 精确休眠;唤醒后,再忙等至整秒边界(如 System.nanoTime() 对齐到下一秒起始),再触发任务。这样既避开系统调度延迟累积,又确保任务在秒级起点准时开始。
- 休眠时长 = 下一目标毫秒值 − 当前 System.currentTimeMillis() − 50(预留小缓冲)
- 若差值 ≤ 0,说明已过期,立即执行并跳到下一个时间点(支持连发)
- 唤醒后用 while(System.nanoTime() % 1_000_000_000 != 0) 微调,直到纳秒时间落到秒边界
跨天与异常场景必须显式处理
单次生成的时间点只覆盖当天,因此循环中需持续判断:当前时间是否已超过列表末尾或进入新一天。一旦触发,就清空旧列表、生成下一日全部时间点。同时,任务执行体应包裹 try-catch,失败不中断主循环,记录日志并继续推进到下一时间点。
- 用 Calendar.getInstance() 或 LocalDate.now().plusDays(1) 快速构造明日时间点列表
- 执行异常时 sleep(1000) 后重试本次任务,最多 3 次;仍失败则跳过,记 warn 日志
- 监听线程中断信号(Thread.interrupted()),收到即 clean exit,释放资源
多个 Cron 表达式合并调度只需统一时间点池
当有多个表达式(如白天常规任务 + 晚间特殊任务),不要分别维护多套循环。统一解析所有表达式,生成一个全局有序时间点集合(TreeSet
- 例如表达式 A 生成 51 个点,表达式 B 生成 8 个点(23 点的特定分钟),合并后可能为 57 个(含重叠)
- 使用 TreeSet 自动排序去重,pollFirst() 获取并移除下一个待执行时间点
- 池子为空时,自动触发下一日全量重建,无需额外状态机

















