Java实现定时任务最简单的3种方法

网友投稿 385 2022-07-24


目录一、Timer二、ScheduledExecutorService三、Spring Task1、开启定时任务2、添加定时任务Cron 表达式知识扩展:分布式定时任务1、ZSet 实现方式2、键空间通知总结

一、Timer

Timer是java自带的定时任务类,实现如下:

public class MyTimerTask {

public static void main(String[] args) {

// 定义一个任务

TimerTask timerTask = new TimerTask() {

@Override

public void run() {

System.out.println("打印当前时间:" + new Date());

}

};

// 计时器

Timer timer = new Timer();

// 开始执行任务 (延迟1000毫秒执行,每3000毫秒执行一次)

timer.schedule(timerTask, 10ErqroT00, 3000);

}

}

Timer 优缺点分析

优点是使用简单,缺点是当添加并执行多个任务时,前面任务的执行用时和异常将影响到后面任务,这边深海建议谨慎使用。

二、ScheduledExecutorService

ScheduledExecutorService 也是Java自带的类,

它可以实现Timer具备的所有功能,并解决了 Timer类存在的问题。

实现如下:

public class MyScheduledExecutorService {

public static void main(String[] args) {

// 创建任务队列 10 为线程数量

ScheduledExecutorService scheduledExecutorService =

Executors.newScheduledThreadPool(10);

// 执行任务

scheduledExecutorService.scheduleAtFixedRate(http://() -> {

System.out.println("打印当前时间:" + new Date());

}, 1, 3, TimeUnit.SECONDS); // 1s 后开始执行,每 3s 执行一次

}

}

ScheduledExecutorService 优缺点分析

优点是,该类是JDK1.5自带的类,使用简单,缺点是该方案仅适用于单机环境。

三、Spring Task

Spring系列框架中Spring Framework自带的定时任务,

使用上面两种方式,很难实现某些特定需求,比如每周一执行某任务,但SpringTask可轻松实现。

以SpringBoot为例来实现:

1、开启定时任务

在SpringBoot的启动类上声明 @EnableScheduling:

@SpringBootApplication

@EnableScheduling //开启定时任务

public class DemoApplication {

// -- --

}

2、添加定时任务

只需使用@Scheduled注解标注即可,

如果有多个定时任务,可以创建多个@Scheduled标注的方法,示例如下:

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

@Component // 把此类托管给 Spring,不能省略

public class TaskUtils {

// 添加定时任务

@Scheduled(cron = "30 40 23 0 0 5") // cron表达式:每周一 23:40:30 执行

public void doTask(){

System.out.println("我是定时任务~");

}

}

Spring Boot 启动后会自动加载并执行定时任务,无需手动操作。

Cron 表达式

Spring Task 的实现需要使用 cron 表达式来声明执行的频率和规则,cron 表达式是由 6 位或者 7 位组成的(最后一位可以省略),每位之间以空格分隔,每位从左到右代表的含义如下:

其中 * 和 ? 号都表示匹配所有的时间。

cron 表达式在线生成地址:https://cron.qqe2.com/

知识扩展:分布式定时任务

上面的方法都是关于单机定时任务的实现,如果是分布式环境可以使用 Redis 来实现定时任务。

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

1、ZSet 实现方式

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

import redis.clients.jedis.Jedis;

import utils.JedisUtils;

import java.time.Instant;

import java.util.Set;

public class DelayQueueExample {

private static final String _KEY = "DelayQueueExample";

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) {

// 当前时间

ErqroT Instant nowInstant = Instant.now();

long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond();

// 上一秒时间

long nowSecond = nowInstant.getEpochSecond();

// 查询当前时间的所有任务

Set data = jedis.zrangeByScore(_KEY, lastSecond, nowSecond);

for (String item : data) {

// 消费任务

System.out.println("消费:" + item);

}

// 删除已经执行的任务

jedis.zremrangeByScore(_KEY, lastSecond, nowSecond);

Thread.sleep(1000); // 每秒查询一次

}

}

}

2、键空间通知

我们可以通过 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() {

@Override

public void onPMessage(String pattern, String channel, String message) {ErqroT

// 接收到消息,执行定时任务

System.out.println("收到消息:" + message);

}

}, _TOPIC);

}

}

总结


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java实现图形化界面的日历
下一篇:Java实现简单的日历界面
相关文章

 发表评论

暂时没有评论,来抢沙发吧~