【015】Dubbo3从0到1系列之定时任务
文章目录
- JDK定时任务管理
- 七、定时任务管理
- 7.1 JDK定时任务管理
- 7.1.1 java.util.TimerTask、java.util.Timer
- 7.1.2 java.util.concurrent.ScheduledExecutorService
- 特点:
- 7.1.3 Java21虚拟线程
- 7.1.4 JDK实现定时任务管理优缺点总结
JDK定时任务管理
相当于复习一下JDK提供的定时任务管理,若已经非常熟悉,则跳过此部分也可以.
七、定时任务管理
7.1 JDK定时任务管理
7.1.1 java.util.TimerTask、java.util.Timer
这是 JDK 最早提供的定时任务支持,适用于简单的定时调度场景。
Timer:调度器,用于安排任务。TimerTask:抽象类,表示一个可被调度的任务(需实现run()方法)
✅ 示例代码
/*** @author: laoren* @date: 2025/10/14 16:40* @description: java.utils.timer* @version: 1.0.0*/
public class T2 {public static void main(String[] args) {Timer timer = new Timer();TimerTask task = new TimerTask() {@Overridepublic void run() {System.out.println("hello world");}};// 延迟 1000ms 后开始,每隔 2000ms 执行一次timer.schedule(task, 1000, 2000);}
}
❌ 缺点:
- 单线程执行:所有任务共享一个线程,如果某个任务执行时间过长或抛出异常,会影响其他任务。
- 不支持复杂的调度(如 cron 表达式)。
- 从 Java 5 开始,推荐使用
ScheduledExecutorService替代。
7.1.2 java.util.concurrent.ScheduledExecutorService
✅ 具体使用方式
import java.util.concurrent.*;public class SchedulerExample {public static void main(String[] args) {// 创建单线程调度器ScheduledExecutorService singleThreadScheduler = Executors.newSingleThreadScheduledExecutor();// 创建多线程调度器ScheduledExecutorService multiThreadScheduler = Executors.newScheduledThreadPool(4);// 使用虚拟线程(JDK 21 新特性)ScheduledExecutorService virtualThreadScheduler =Executors.newScheduledThreadPool(0, Thread.ofVirtual().factory());}
}
创建线程池,指定线程工厂,此时可以传入虚拟线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, // 线程核心数ThreadFactory threadFactory // 创建线程的工厂
) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
这是 Java 5 引入的更强大、更灵活的定时任务调度接口,属于 java.util.concurrent 包。
特点:
- 支持多线程(可配置线程池大小)。
- 提供更丰富的调度方法(固定延迟、固定频率等)。
- 异常不会影响其他任务(每个任务独立运行)。
- 更适合生产环境。
✅ 示例代码
package cn.tcmeta.interfaces.test;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** @author: laoren* @description: TODO* @version: 1.0.0*/
public class T3 {public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(2);scheduledExecutorService.scheduleAtFixedRate(() -> {System.out.println("hello world");// 延迟 1 秒后,每隔 2 秒执行一次}, 1, 2, TimeUnit.SECONDS);}
}

✅ 常用方法
// 1.
schedule(Runnable command, long delay, TimeUnit unit):延迟执行一次。
// 2.
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):固定频率执行。
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):固定延迟执行(上次执行结束后再延迟)。
✅ 对比总结
| 特性 | Timer | ScheduledExecutorService |
|---|---|---|
| 线程模型 | 单线程 | 多线程(可配置) |
| 异常处理 | 一个任务异常会导致整个 Timer 停止 | 任务独立,互不影响 |
| 调度灵活性 | 低 | 高 |
| 推荐使用 | ❌(已过时) | ✅(推荐) |
✅ 补充说明
如果需要更复杂的调度(如 cron 表达式、持久化、分布式调度等),JDK 自带的工具就不够用了,通常会使用第三方框架,例如:
- Quartz:功能强大的开源调度框架。
- Spring Task(基于
@Scheduled):Spring 提供的简化调度。 - XXL-JOB / Elastic-Job:适用于分布式场景。
7.1.3 Java21虚拟线程
简化高并发编程。虚拟线程由 JVM 管理,轻量级、可扩展性强,特别适合 I/O 密集型任务。
虽然虚拟线程本身 不是调度器,但可以结合 ScheduledExecutorService 或其他机制,用虚拟线程来执行定时任务,从而在高并发定时任务场景下获得更好的资源利用率和吞吐量。
✅ 方式一: 使用 Executors.newVirtualThreadPerTaskExecutor() + ScheduledExecutorService(推荐)
⚠️ 注意:ScheduledExecutorService 本身不直接支持虚拟线程,但你可以:
- 用传统调度器(如 newScheduledThreadPool)来触发任务;
- 任务提交到虚拟线程执行器中运行。
🆕 创建线程池的时候,可以通过传入线程池创建工作来使用虚拟线程
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, // 线程核心数ThreadFactory threadFactory // 创建线程的工厂 ) {return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); }
✅ 使用示例
定时触发 + 虚拟线程执行
package cn.tcmeta.interfaces.test;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class VirtualThreadScheduledTask {public static void main(String[] args) throws InterruptedException {// 1. 创建一个传统调度器(用于定时触发)ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);// 2. 创建虚拟线程执行器(每个任务一个虚拟线程)ExecutorService virtualExecutor = Executors .newVirtualThreadPerTaskExecutor();// 定义要处理的任务Runnable task = () -> {System.out.println("任务执行中,线程: " + Thread.currentThread());// 模拟 I/O 或业务处理(虚拟线程在此阻塞不会浪费 OS 线程)try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("任务完成: " + Thread.currentThread());};// 每隔 2 秒调度一次,但实际工作交给虚拟线程scheduler.scheduleAtFixedRate(() -> {virtualExecutor.submit(task);}, 0, 2, TimeUnit.SECONDS);// 主线程等待(生产环境中应优雅关闭)Thread.sleep(10_000);scheduler.shutdown();virtualExecutor.close(); // Java 21+ 支持 AutoCloseable}
}
7.1.4 JDK实现定时任务管理优缺点总结
✅✅✅ 优点
- 原生无依赖,降低部署成本
- API简洁直观、学习成本低
- 线程模型灵活(仅 ScheduledExecutorService)
- 时间精度更可靠(仅 ScheduledExecutorService)
- 异常隔离(仅 ScheduledExecutorService)
❌❌❌ 缺点
JDK 自带定时任务的局限性主要体现在功能简陋、缺乏高级能力,无法满足复杂或生产级场景需求:
-
缺乏复杂调度能力,仅支持基础模式
- 仅支持三种基础调度逻辑,无法满足 “按日历时间调度”“多周期组合” 等需求:
- 不支持cron 表达式(如 “每周一凌晨 2 点执行”“每月最后一天 18 点执行”),无法实现灵活的时间规则。
- 无 “任务优先级”:所有任务同等优先级,无法指定核心任务优先执行(如 “数据备份任务” 优先于 “日志清理任务”)。
- 无 “动态周期”:任务提交后,无法修改执行周期(如原本每隔 5 分钟执行,需改为每隔 10 分钟,只能取消原任务后重新提交)
- 仅支持三种基础调度逻辑,无法满足 “按日历时间调度”“多周期组合” 等需求:
-
任务管理能力弱
-
仅支持 “提交” 和 “取消”(通过
Future.cancel()),无暂停 / 恢复、任务依赖(如 “任务 A 执行完后再执行任务 B”)等功能。 -
无法查询任务状态:没有 API 获取已提交任务的列表、执行状态(如 “等待中”“执行中”“已失败”)、执行次数、耗时等信息,调试和问题排查困难。
-
-
无监控与告警机制
- 无内置监控能力:无法统计任务成功率、平均执行耗时、失败次数等关键指标,需手动埋点(如捕获异常后打印日志、上报监控平台)。
- 任务失败无默认告警:若任务抛出异常,仅会终止当前任务(ScheduledExecutorService)或所有任务(Timer),无邮件、短信等告警通知,需开发者自行实现。
-
无持久化与故障恢复
- 任务仅存于内存:应用重启、崩溃或服务器宕机后,未执行的任务会完全丢失,无法恢复。
- 不支持分布式部署:无法在多实例集群中协调任务(如避免同一任务在多个实例上重复执行),需额外引入分布式锁、注册中心等组件,复杂度大幅提升。
-
Timer的固定缺陷
- 虽然 ScheduledExecutorService 已解决 Timer 的问题,但仍有开发者误用 Timer,需明确其缺陷:
- 单线程串行:所有任务在一个线程中执行,若一个任务执行时间过长(如耗时 10 分钟),后续任务会全部延迟。
- 异常导致全局崩溃:单个任务抛出未捕获异常,Timer 线程终止,所有后续任务作废。
- 时间精度受系统时间影响:依赖
System.currentTimeMillis(),系统时间回拨可能导致任务重复执行。
- 虽然 ScheduledExecutorService 已解决 Timer 的问题,但仍有开发者误用 Timer,需明确其缺陷:
-
固定速率/ 延迟的局限性(ScheduledExecutorService 仍存在)
- 固定速率(scheduleAtFixedRate):若任务执行时间 > 周期(如周期 3 秒,任务执行 5 秒),线程池会在任务结束后立即执行下一次任务(相当于 “追赶执行”),可能导致任务并发执行(线程池线程足够时),引发线程安全问题;若线程池线程不足,任务会排队,实际周期变长。
- 固定延迟(scheduleWithFixedDelay):仅保证 “上一个任务结束到下一个任务开始” 的间隔固定(如延迟 2 秒),无法保证 “任务开始时间间隔” 固定(如第一个任务 10:00 开始,执行 5 秒,第二个任务 10:05 开始,间隔 5 秒而非 2 秒),不适合对开始时间精度要求高的场景。
适用场景总结
JDK 自带的定时任务(尤其是 ScheduledExecutorService)仅适合单机、轻量级、简单调度需求的场景,例如:
- 应用内的简单定时任务(如每隔 1 分钟清理本地临时文件、延迟 5 秒发送通知)。
- 对任务可靠性、监控、分布式无要求的场景(如测试环境、工具类应用)。
若需满足以下需求,建议使用第三方框架(如 Quartz、XXL-Job、Elastic-Job):
- 复杂调度(如 cron 表达式、任务优先级)。
- 分布式部署(避免任务重复执行)。
- 任务持久化与故障恢复(应用重启后任务不丢失)。
- 监控、告警与详细的任务管理。
JDK 提供的 java.util.Timer 和 DelayedQueue 等工具类,可以帮助我们实现简单的定时任务管理,其底层实现使用的是堆这种数据结构,存取操作的复杂度都是 O(nlog(n)),无法支持大量的定时任务。推荐使用【时间轮】
只为引出【时间轮】, 🚀🚀🚀🚀 … enjoy it!!!
