You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何配置SingleThreadScheduledExecutor实现分时段定时任务

基于SingleThreadScheduledExecutor实现英国时区跨时段定时调度

你当前编写的固定15分钟延迟的代码无法满足分时段调整执行频率的需求,scheduleWithFixedDelay 一旦启动就无法动态修改执行间隔,最适合单线程调度器的实现方案是每次任务执行完成后动态计算下一次间隔,单次提交下一次调度任务,实现逻辑如下。

核心实现逻辑

  • 所有时间计算统一使用Europe/London时区,自动适配英国夏令时/冬令时切换,避免时间偏移
  • 任务执行完成后,取当前英国时间判断所属时间窗口,动态选择间隔时长:
    • 处于每日8:00-10:00窗口时,下一次任务间隔15分钟
    • 处于其余时段时,下一次任务间隔1小时
  • 增加异常兜底逻辑,避免业务任务抛出未捕获异常导致整个调度链中断
  • 单线程调度器天然保证任务串行执行,不会出现并发重复跑任务的问题

可直接运行的Scala代码

import java.time.{Instant, LocalTime, ZoneId}
import java.util.concurrent.{Executors, ScheduledExecutorService, TimeUnit}
import java.util.concurrent.atomic.AtomicBoolean

// 线程池沿用你原有配置
val pollingExecutor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
  new NamedThreadFactory("MyTestScheduledThread")
)
// 英国时区常量
private val UK_ZONE: ZoneId = ZoneId.of("Europe/London")
// 高频窗口起止时间
private val HIGH_FREQ_START: LocalTime = LocalTime.of(8, 0)
private val HIGH_FREQ_END: LocalTime = LocalTime.of(10, 0)
// 调度运行标记,服务关闭时置false即可停止调度
private val isRunning = new AtomicBoolean(true)

// 调度入口
def startScheduling(): Unit = {
  val task = new Runnable {
    override def run(): Unit = {
      try {
        // 执行业务作业
        new MyRunnableJob().run()
      } catch {
        case e: Exception =>
          // 按需打印日志、做告警,避免异常吞掉导致调度中断
          e.printStackTrace()
      } finally {
        if (!isRunning.get()) return
        // 计算下一次执行间隔
        val currentUkTime = Instant.now().atZone(UK_ZONE).toLocalTime
        val nextDelayMins = if (
          currentUkTime.isAfter(HIGH_FREQ_START) && currentUkTime.isBefore(HIGH_FREQ_END)
        ) 15L else 60L
        // 提交下一次任务
        pollingExecutor.schedule(this, nextDelayMins, TimeUnit.MINUTES)
      }
    }
  }
  // 首次提交,初始延迟0秒即启动后立刻执行第一次,可按需调整
  pollingExecutor.schedule(task, 0, TimeUnit.SECONDS)
}

// 启动调度
startScheduling()

// 服务停机钩子,优雅关闭线程池(按需开启)
// Runtime.getRuntime.addShutdownHook(new Thread(() => {
//   isRunning.set(false)
//   pollingExecutor.shutdown()
//   pollingExecutor.awaitTermination(10, TimeUnit.SECONDS)
// }))

可选调整说明

如果需要任务严格对齐时间点执行(比如8:00、8:15、9:00这类准点触发,而非以上一次任务执行完为起点算间隔),只需要修改finally块里的延迟计算逻辑,算出当前时间到下一个目标触发点的时间差作为延迟即可,其余调度逻辑不需要改动。

内容的提问来源于stack exchange,提问作者Dewang Dave

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:12:56