当前位置: 首页 > news >正文

Java中实现定时任务执行的方式总结

一、概述

关于在Java项目中实现定时任务执行的几种方式总结如下:

  • 单进程实现定时任务主要有以下几种方式:

1. Timer 和 TimerTask(JDK 原生)

2. ScheduledExecutorService(JDK 1.5+,推荐)

3. Spring Task(Spring 框架支持)

4. Quartz(企业级任务调度框架)

5. DelayQueue 阻塞队列

  • 分布式定时任务

1、使用Redis来实现定时任务

2、使用xxl-job实现定时任务

二、详情

1. Timer 和 TimerTask(JDK 原生)

Java中的Timer类是一个定时调度器,用于在指定的时间点执行任务。Timer 类用于实现定时任务,最大的好处就是他的实现非常简单,特别的轻量级,因为它是Java内置的,所以只需要简单调用就行了。

public class MyTimerTask {public static void main(String[] args) {// 定义一个任务TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("Run timerTask:" + new Date());}};// 计时器Timer timer = new Timer();// 添加执行任务(延迟 1s 执行,每 3s 执行一次)timer.schedule(timerTask, 1000, 3000);}
}
优点缺点
  • 简单易用,适合单线程任务调度。
  • 基于绝对时间(Date)执行任务。
  • Timer内部是单线程执行任务的,如果某个任务执行时间较长,会影响后续任务的执行
  • 如果任务抛出未捕获异常,将导致整个 Timer 线程终止,影响其他任务的执行
  • Timer 无法提供高精度的定时任务。因为系统调度和任务执行时间的不确定性,可能导致任务执行的时间不准确。
  • 虽然可以使用 cancel 方法取消任务,但这仅仅是将任务标记为取消状态,仍然会在任务队列中占用位置,无法释放资源。这可能导致内存泄漏。
  • 当有大量任务时,Timer 的性能可能受到影响,因为它在每次扫描任务队列时都要进行时间比较。
  • Timer执行任务完全基于JVM内存,一旦应用重启,那么队列中的任务就都没有了



 

2. ScheduledExecutorService(JDK 1.5+,推荐)

ScheduledExecutorService基于线程池(ThreadPoolExecutor),支持多任务并发执行。相比 Timer,更稳定、更灵活。

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);// 延迟 1s 后执行
scheduler.schedule(() -> {System.out.println("Task executed once at: " + new Date());
}, 1, TimeUnit.SECONDS);// 延迟 1s 后执行,之后每 2s 执行一次
scheduler.scheduleAtFixedRate(() -> {System.out.println("Fixed-rate task executed at: " + new Date());
}, 1, 2, TimeUnit.SECONDS);
优点缺点
  • 是 JDK 1.5 之后自带的 API,因此使用起来也比较方便
  • 执行任务时使用线程池管理,不会造成任务间的相互影响
  • 支持 scheduleAtFixedRate(固定频率)和 scheduleWithFixedDelay(固定间隔)。
  • 需要配置线程池
  • 如果任务抛出异常,后续任务 会被取消!需在任务内捕获异常。

3. Spring Task(Spring 框架支持)

适合 Spring/Spring Boot 项目,基于 ScheduledExecutorService,提供注解式任务调度。

@EnableScheduling
@SpringBootApplication
public class SpringbootApplication {public static void main(String[] args) {SpringApplication.run(SpringbootApplication.class, args);}}@Component
public class TimerTask {@Scheduled(cron = "0 0 0 0 7 *")public void task() {System.out.println("定时任务...");}}
优点缺点
  • 注解驱动,开发简单。
  • 支持 Cron 表达式(如 0 0 12 * * ? 表示每天中午 12 点执行)。
只适合 Spring/Spring Boot 项目

4. Quartz(企业级任务调度框架)

Quartz是OpenSymphony开源组织在Job scheduling领域的一个Java实现的开源项目。

// 1. 定义 Job
public class MyJob implements Job {@Overridepublic void execute(JobExecutionContext context) {System.out.println("Quartz Job executed at: " + new Date());}
}// 2. 配置 Trigger 和 Scheduler
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("myJob", "group1").build();Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1").startNow().withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5) // 每 5s 执行一次.repeatForever()).build();scheduler.scheduleJob(job, trigger);
scheduler.start();
优点缺点
  • 支持数据库持久化,重启后可恢复调度
  • 支持动态调整任务(如暂停、恢复、修改执行时间)
  • 分布式需要使用数据库

5. DelayQueue 阻塞队列

DelayQueue是一个带有延迟时间的无界阻塞队列,它的元素必须实现Delayed接口。当从DelayQueue中取出一个元素时,如果其延迟时间还未到达,则会阻塞等待,直到延迟时间到达。因此,我们可以通过将任务封装成实现Delayed接口的元素,将其放入DelayQueue中,再使用一个线程不断地从DelayQueue中取出元素并执行任务,从而实现定时任务的调度。

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;public class DelayedTask implements Delayed {private final String taskName;private final long executeTime; // 执行时间(毫秒时间戳)public DelayedTask(String taskName, long delayMs) {this.taskName = taskName;this.executeTime = System.currentTimeMillis() + delayMs;}@Overridepublic long getDelay(TimeUnit unit) {// 返回剩余延迟时间long remainingDelay = executeTime - System.currentTimeMillis();return unit.convert(remainingDelay, TimeUnit.MILLISECONDS);}@Overridepublic int compareTo(Delayed other) {// 按执行时间排序(早到期者优先)return Long.compare(this.executeTime, ((DelayedTask) other).executeTime);}public void execute() {System.out.println("Executing task: " + taskName + " at " + new Date(executeTime));}
}public class DelayQueueScheduler {private static final DelayQueue<DelayedTask> queue = new DelayQueue<>();public static void main(String[] args) throws InterruptedException {// 提交延迟任务queue.put(new DelayedTask("Task 1", 3000)); // 3s 后执行queue.put(new DelayedTask("Task 2", 1000)); // 1s 后执行queue.put(new DelayedTask("Task 3", 5000)); // 5s 后执行// 启动消费者线程处理任务while (!queue.isEmpty()) {DelayedTask task = queue.take(); // 阻塞直到有任务到期task.execute();}}
}
优点缺点
  • 精确延迟、无第三方依赖
  • 只能单进程内使用

6、使用 Redis 实现延迟任务

使用 Redis 实现延迟任务的方法大体可分为两类:通过 ZSet 的方式和键空间通知的方式。

① ZSet 实现方式

通过 ZSet 实现定时任务的思路是,将定时任务存放到 ZSet 集合中,并且将过期时间存储到 ZSet 的 Score 字段中,然后通过一个无线循环来判断当前时间内是否有需要执行的定时任务,如果有则进行执行,具体实现代码如下:

import redis.clients.jedis.Jedis;
import utils.JedisUtils;
import java.time.Instant;
import java.util.Set;public class DelayQueueExample {// zset keyprivate static final String _KEY = "myTaskQueue";public static void main(String[] args) throws InterruptedException {Jedis jedis = JedisUtils.getJedis();// 30s 后执行long delayTime = Instant.now().plusSeconds(30).getEpochSecond();jedis.zadd(_KEY, delayTime, "order_1");// 继续添加测试数据jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_2");jedis.zadd(_KEY, Instant.now().plusSeconds(2).getEpochSecond(), "order_3");jedis.zadd(_KEY, Instant.now().plusSeconds(7).getEpochSecond(), "order_4");jedis.zadd(_KEY, Instant.now().plusSeconds(10).getEpochSecond(), "order_5");// 开启定时任务队列doDelayQueue(jedis);}/*** 定时任务队列消费* @param jedis Redis 客户端*/public static void doDelayQueue(Jedis jedis) throws InterruptedException {while (true) {// 当前时间Instant nowInstant = Instant.now();long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond(); // 上一秒时间long nowSecond = nowInstant.getEpochSecond();// 查询当前时间的所有任务Set<String> data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);for (String item : data) {// 消费任务System.out.println("消费:" + item);}// 删除已经执行的任务jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);Thread.sleep(1000); // 每秒查询一次}}
}
② 键空间通知

我们可以通过 Redis 的键空间通知来实现定时任务,它的实现思路是给所有的定时任务设置一个过期时间,等到了过期之后,我们通过订阅过期消息就能感知到定时任务需要被执行了,此时我们执行定时任务即可。

默认情况下 Redis 是不开启键空间通知的,需要我们通过 config set notify-keyspace-events Ex 的命令手动开启,开启之后定时任务的代码如下:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
import utils.JedisUtils;public class TaskExample {public static final String _TOPIC = "__keyevent@0__:expired"; // 订阅频道名称public static void main(String[] args) {Jedis jedis = JedisUtils.getJedis();// 执行定时任务doTask(jedis);}/*** 订阅过期消息,执行定时任务* @param jedis Redis 客户端*/public static void doTask(Jedis jedis) {// 订阅过期消息jedis.psubscribe(new JedisPubSub() {@Overridepublic void onPMessage(String pattern, String channel, String message) {// 接收到消息,执行定时任务System.out.println("收到消息:" + message);}}, _TOPIC);}
}

7、使用xxl-job实现定时任务

xxl-job是一款分布式定时任务调度平台,可以实现各种类型的定时任务调度,如定时执行Java代码、调用HTTP接口、执行Shell脚本等。xxl-job采用分布式架构,支持集群部署,可以满足高并发、大数据量的任务调度需求。

三、开源项目

1、https://gitee.com/xuxueli0323/xxl-job

2、https://gitee.com/dromara/disjob

3、https://gitee.com/KFCFans/PowerJob

4、https://gitee.com/aizuda/snail-job

5、https://gitee.com/elasticjob/elastic-job

http://www.lryc.cn/news/603092.html

相关文章:

  • 反欺诈系统:Oracle 到 ES 迁移实战
  • 【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博文章数据可视化分析-点赞区间实现
  • Java类加载机制详解
  • AI coding汇总持续更新
  • STM32启动流程
  • 【学习路线】Android开发2025:从入门到高级架构师
  • Unity_UI_NGUI_锚点组件
  • 【java面试day7】redis分布式锁
  • SpringBoot 发送邮件
  • 五自由度磁悬浮轴承转子不平衡质量的高性能控制策略全解析
  • 算法训练营day34 动态规划② 62.不同路径、63. 不同路径 II、343整数拆分、96.不同的二叉搜索树
  • Java响应式编程
  • ATF 运行时服务
  • ros2的package.xml和rosdep
  • 基于深度学习的医学图像分析:使用3D CNN实现肿瘤检测
  • 第十天:字符菱形
  • 一个Pycharm窗口添加多个项目来满足运行多个项目的需求
  • DDoS攻击防御:从5G到T级防护方案全对比
  • 企业级日志分析系统ELK
  • Python动态规划:从基础到高阶优化的全面指南(3)
  • 历史版本的vscode下载地址
  • 实验-静态路由
  • 智慧工地系统:科技赋能建筑新未来
  • 学习dify:一个开源的 LLM 应用开发平台
  • 【GitHub Workflows 基础(二)】深入理解 on、jobs、steps 的核心语法与执行逻辑
  • 【2025/07/28】GitHub 今日热门项目
  • 【iOS】类和分类的加载过程
  • LNMP架构+wordpress实现动静分离
  • Cacti RCE漏洞复现
  • 四、计算机组成原理——第1章:计算机系统概述