ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor 用于延迟执行和周期执行任务,是 Timer 的现代替代方案。
# 1. 基本能力
| 方法 | 作用 |
|---|---|
schedule | 延迟执行一次 |
scheduleAtFixedRate | 固定频率执行 |
scheduleWithFixedDelay | 固定延迟执行 |
创建:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
生产中也可以直接构造并配置线程工厂和拒绝策略。
# 2. 延迟执行
scheduler.schedule(() -> {
clean();
}, 10, TimeUnit.SECONDS);
表示 10 秒后执行一次。
# 3. 固定频率
scheduler.scheduleAtFixedRate(
this::report,
0,
5,
TimeUnit.SECONDS
);
时间模型:
计划时间:0s, 5s, 10s, 15s
如果任务执行时间超过周期,下一次执行不会并发重叠,但会尽快补上。
# 4. 固定延迟
scheduler.scheduleWithFixedDelay(
this::sync,
0,
5,
TimeUnit.SECONDS
);
时间模型:
任务结束
│
▼
等待 5 秒
│
▼
下一次开始
固定延迟更适合任务耗时不稳定的场景。
# 5. 异常影响
周期任务如果抛出未捕获异常,后续执行会被取消。
scheduler.scheduleAtFixedRate(() -> {
try {
doWork();
} catch (Exception e) {
log.error("定时任务失败", e);
}
}, 0, 1, TimeUnit.MINUTES);
周期任务内部应捕获并记录异常,避免整个任务静默停止。
# 6. 与 Timer 对比
| 对比项 | Timer | ScheduledThreadPoolExecutor |
|---|---|---|
| 线程数 | 单线程 | 可多线程 |
| 异常影响 | 一个任务异常可能终止 Timer 线程 | 单任务失败不影响线程池整体 |
| 时间基准 | 较老设计 | 更现代 |
| 推荐度 | 不推荐新代码 | 推荐 |
# 7. 关闭
scheduler.shutdown();
服务关闭时要停止定时线程池,否则 JVM 可能无法退出,任务也可能继续执行。
# 8. 使用建议
- 线程命名。
- 捕获周期任务异常。
- 区分固定频率和固定延迟。
- 任务执行时间不要超过周期太多。
- 分布式系统中避免多节点重复执行同一任务,必要时加分布式锁或调度平台。
# 专家实践与边界
ScheduledThreadPoolExecutor 适合 JVM 进程内的轻量定时任务,但它不是分布式调度系统。任务耗时、异常、重入和集群多实例重复执行都要单独设计。
延迟队列
│ 到期
▼
工作线程执行任务
│
├─ 一次性任务:结束
└─ 周期任务:计算下次执行时间
| 方法 | 调度方式 | 注意点 |
|---|---|---|
schedule | 延迟一次执行 | 适合超时清理 |
scheduleAtFixedRate | 固定频率 | 任务慢会追赶执行 |
scheduleWithFixedDelay | 固定延迟 | 上次结束后再等 |
| 异常处理 | 默认异常会终止后续周期 | 任务内部必须捕获 |
生产中定时任务要有日志、耗时指标、异常告警和互斥策略。集群部署时,如果只能有一个节点执行,需要使用分布式锁或调度平台。
# Tips 快问快答
Q:固定频率和固定延迟区别是什么? A:固定频率按计划时间点触发,固定延迟从上次结束后再等待指定时间。
Q:周期任务抛异常会怎样? A:后续执行可能被取消。
Q:为什么不推荐 Timer?
A:单线程、异常影响大,现代代码用 ScheduledThreadPoolExecutor。
Q:任务会并发重叠执行吗? A:同一个周期任务通常不会重叠执行。
Q:定时任务要不要捕获异常? A:要,避免任务静默停止。
Q:分布式系统定时任务要注意什么? A:多节点重复执行,需要调度协调或分布式锁。
Q:关闭应用时要处理 scheduler 吗? A:要调用 shutdown。
Q:耗时不稳定任务用哪种周期方式?
A:通常用 scheduleWithFixedDelay。
上次更新: 2026/06/25, 14:19:18