ScheduledExecutorService比Timer更稳,因其基于线程池,单任务异常不影响其他任务,支持并行调度、相对时间计时、精细生命周期控制;Timer为单线程,异常导致全局停摆,且依赖系统绝对时间。
因为Timer是单线程的,一个任务抛出未捕获异常(比如 RuntimeException),整个Timer就“死机”,后续所有任务全停;而 ScheduledExecutorService 基于线程池,单个任务崩溃不会影响其他任务执行,异常被吞掉后调度照常进行。
ScheduledExecutorService 可配多线程(如 newScheduledThreadPool(3)),任务可并行,互不干扰ScheduledExecutorService 基于相对纳秒计时,更可靠cancel() 后无法再提交新任务;ScheduledExecutorService 支持 shutdown()、shutdownNow() 和 Future.cancel(true) 精细控制这两个方法名字像,行为却完全不同,选错会导致任务“越积越多”或“间隔失控”。
scheduleAtFixedRate:按“开始时间”对齐。比如 initialDelay=1s、period=3s,那它会在 t=1s、4s、7s… 准时启动任务。如果某次任务执行超时(比如花了 5s),下一次仍会在 t=10s 启动——意味着两个任务可能并发运行scheduleWithFixedDelay:按“结束时间”计算。同样参数下,第一次在 t=1s 开始,执行完(比如 t=6s),下一次才在 t=11s 启动。永远保证前一个彻底结束,再等 delay 时间才启下一个scheduleWithFixedDelay;心跳上报(必须每 N 秒准时发一次)→ 用 scheduleAtFixedRate
看似简单,但生产环境一不留神就内存泄漏或线程堆积。
Executors.newSingleThreadScheduledExecutor() 做全局调度器——它返回的实例不支持
shutdown() 的优雅关闭语义,建议显式构造 ScheduledThreadPoolExecutor
schedule* 方法都返回一个 ScheduledFuture,如果要动态取消,请保存它;否则任务会一直跑下去,哪怕主线程结束了shutdown() 或 shutdownNow() → JVM 不会自动退出,应用变成“假退出”状态(尤其 Spring Boot 应用里容易忽略)ScheduledExecutorService,且其中一个长期阻塞(如 IO 未设超时),其他任务会被卡住——应按任务类型隔离线程池它只是 JDK 提供的轻量级工具,不是万能解。遇到这些情况,该换就得换。
ScheduledExecutorService 不支持,得上 Quartz 或 @Scheduled(cron = "...")(Spring)ScheduledExecutorService 是纯本地的,无协调能力;必须引入分布式锁或专用调度中心一句话收尾:如果你的任务是单机、短周期、无状态、可丢失、不需要人工干预,ScheduledExecutorService 就是最简最稳的选择;一旦跨出这个边界,别硬扛,及时切到更专业的方案。