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

不使用java.util.Timer:基于ExecutionContext实现延迟Future

最简实现方案

首先纠正你函数签名里的拼写笔误:ExecutoionContext正确拼写为ExecutionContext。
需要明确的是,普通ExecutionContext仅负责提交后立刻触发的任务执行,本身没有内置延迟调度能力,因此实现需要内置一个全局共享的调度组件,完全隔离对客户端的暴露,同时彻底放弃对java.util.Timer的依赖。

核心实现代码

import scala.concurrent.{Promise, Future, ExecutionContext}
import scala.util.Success
import scala.concurrent.duration.FiniteDuration
import java.util.concurrent.{Executors, ScheduledExecutorService, TimeUnit}

// 全局共享的单例调度器,仅在类加载时初始化一次
private val sharedScheduler: ScheduledExecutorService = 
  Executors.newSingleThreadScheduledExecutor(runnable => {
    val worker = new Thread(runnable, "delayed-future-scheduler")
    worker.setDaemon(true) // 设为守护线程,不阻碍JVM正常退出
    worker
  })

def delayedFuture(delay: FiniteDuration)
                 (implicit ec: ExecutionContext): Future[Unit] = {
  val promise = Promise[Unit]()
  // 提交延迟任务,到点直接完成Promise
  sharedScheduler.schedule(
    () => promise.complete(Success(())),
    delay.length,
    delay.unit
  )
  promise.future
}

实现说明

  • 全程未使用java.util.Timer,基于JDK自带的ScheduledExecutorService完成延迟调度,可靠性远高于Timer:单个任务抛出未捕获异常不会导致整个调度线程崩溃,调度精度更高,支持主动关闭
  • 调度器为全局单例,不会因每次调用delayedFuture新建独立线程,完全满足封装要求——客户端不会感知到内部调度组件的存在,只需要按照原有隐式参数规则传入ExecutionContext即可
  • 特意将调度线程设为守护线程,避免应用退出时被闲置的调度线程阻塞

常见错误避坑

绝对不要为了“完全复用传入的ExecutionContext做调度”,写阻塞式的实现,比如:

// 错误实现,生产环境严禁使用
def delayedFuture(delay: FiniteDuration)(implicit ec: ExecutionContext) = {
  val p = Promise[Unit]()
  ec.execute { () =>
    Thread.sleep(delay.toMillis)
    p.success(())
  }
  p.future
}

这种写法会让EC的工作线程长期被sleep占用,高并发场景下会快速耗尽线程池,导致所有依赖该EC的异步逻辑全部阻塞,是非常典型的性能坑点。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 18:54:06