定时任务的应用场景其实蛮常见的,比如说:
- 数据备份
- 订单未支付则自动取消
- 定时爬取数据
- 定时推送信息
- 定时发布文章
- 等等(想不出来了,只能等等来凑, ,反正只要等的都需要定时,怎么样,这波圆场可以吧)
编程喵 实战项目里需要做一个定时发布文章的功能,一开始我想用 Spring Task,于是研究了一番,发现 Spring Task 用起来确实简单,但对于复杂业务却也无能为力。
于是我就把注意力放到了 Quartz 上面,这是一款老而弥坚的开源任务调度框架。
记得我在 14 年开发大宗期货交易平台的时候就用到了它,每天凌晨定时需要统计一波交易数据,生成日报报表,当时配合 Cron 表达式用的。
可惜后来平台稳定了,新的政策出来了,直接把大宗期货交易灭了。于是我发财的机会也随着破灭了。想想都觉得可惜,哈哈哈。
时光荏苒,Quartz 发展到现在,已经可以和 Spring Boot 项目无缝衔接了,今天我们就来实战一把。
Timer
JDK 1.3 就开始支持的一种定时任务的实现方式。内部通过 TaskQueue 的类来存放定时任务,用起来比较简单,但缺陷比较多,比如说一个 Timer 就会起一个线程,任务多了性能就非常差,再比如说如果执行任务期间某个 TimerTask 耗时比较久,就会影响其他任务的调度。
@Slf4j
public class TimerDemo {
public static void main(String[] args) {
TimerTask task = new TimerTask() {
@Override
public void run() {
log.debug("当前时间{}线程名称{}", DateTime.now(),
Thread.currentThread().getName());
}
};
log.debug("当前时间{}线程名称{}", DateTime.now(),
Thread.currentThread().getName());
Timer timer = new Timer("TimerDemo");
timer.schedule(task,1000L);
}
}
代码跑起来后的日志如下所示:
13:11:45.268 [main] DEBUG top.springtask.TimerDemo - 当前时间2022-04-27 13:11:45线程名称main
13:11:46.280 [TimerDemo] DEBUG top.springtask.TimerDemo - 当前时间2022-04-27 13:11:46线程名称TimerDemo
ScheduledThreadPoolExecutor
JDK 1.5 开始提供的的定时任务,它继承了 ThreadPoolExecutor,实现了 ScheduledExecutorService 接口,所以支持并发场景下的任务执行。同时,优化了 Timer 的缺陷。不过,由于使用了队列来实现定时器,就有出入队列、调整堆等操作,所以定时不是非常非常准确(吹毛求疵)。
@Slf4j
public class ScheduledThreadPoolExecutorDemo {
public static void main(String[] args) throws InterruptedException {
TimerTask task = new TimerTask() {
@Override
public void run() {
log.debug("当前时间{}线程名称{}", DateTime.now(),
Thread.currentThread().getName());
}
};
log.debug("当前时间{}线程名称{}", DateTime.now(),
Thread.currentThread().getName());
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);
executorService.scheduleAtFixedRate(task, 1000L,1000L, TimeUnit.MILLISECONDS);
Thread.sleep(1000+1000*4);
executorService.shutdown();
}
}
输出结果如下所示:
14:43:41.740 [main] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:41线程名称main
14:43:42.752 [pool-1-thread-1] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:42线程名称pool-1-thread-1
14:43:43.748 [pool-1-thread-1] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:43线程名称pool-1-thread-1
14:43:44.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:44线程名称pool-1-thread-2
14:43:45.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:45线程名称pool-1-thread-2
14:43:46.749 [pool-1-thread-2] DEBUG top.springtask.ScheduledThreadPoolExecutorDemo - 当前时间2022-04-27 14:43:46线程名称pool-1-thread-2
Spring Task
Spring Task 是 Spring 提供的轻量级定时任务工具,也就意味着不需要再添加第三方依赖了,相比其他第三方类库更加方便易用。
好像关于 Spring Task,没有其他废话可说了,我们来直接上手。
第一步,新建配置类 SpringTaskConfig,并添加 @EnableScheduling注解开启 Spring Task。
@Configuration
@EnableScheduling
public class SpringTaskConfig {
}
当然了,也可以不新建这个配置类,直接在主类上添加 @EnableScheduling 注解。
@SpringBootApplication
@EnableScheduling
public class CodingmoreSpringtaskApplication {
public static void main(String[] args) {
SpringApplication.run(CodingmoreSpringtaskApplication.class, args);
}
}
第二步