SpringTask的基本使用
工作中经常会用到定时任务,有的用linux的crontab来实现系统级别的定时调用,当然这种只能调用脚本,不能在我们的程序中实现高度灵活的配置。定时任务的实现有很多,我之前也做过一些笔记,因为在分布式应用中,定时需要小心处理,否则会很容易地出现数据错乱,因此出现了很多适用于分布式场景定时器。当然分布式不在本文讨论范围,这里只想聊聊简单的单机应用,而且是最简单的Spring Task。
一、基本使用
使用起来十分简单,在ssm的工程中不需要额外引入其他的依赖即可使用。因为已经在Spring-Context中集成。

第一步是配置文件中开启定时任务的注解:
1 | <!--引入spring task定时任务--> |
在头部引入相应的DTD约束文件:
1 | xmlns:task="http://www.springframework.org/schema/task" |
配置方面就结束了。
下面写一个定时任务吧:
1 |
|
对于这里的cron表达式,相信大家都知道了,按照一定的匹配规则即可实现比较复杂的定时场景。当然,可以使用可视化的页面来配置:http://cron.qqe2.com/
二、另一种实现:Timer
在这个最简单的应用中,就是实现每隔几分钟来做一些事情的简单场景,还可以使用JDK自带的Timer来实现。下面给出一个最简单的使用:
1 | Timer timer = new Timer(); |
Timer 可以按计划执行重复的任务或者定时执行指定任务,这是因为 Timer 内部利用了一个后台线程 TimerThread 有计划地执行指定任务。
Timer:是一个实用工具类,该类用来调度一个线程(schedule a thread),使它可以在将来某一时刻执行。Java的Timer类可以调度一个任务运行一次或定期循环运行。Timer tasks should complete quickly.即定时器中的操作要尽可能花费短的时间。TimerTask:一个抽象类,它实现了Runnable接口。我们需要扩展该类以便创建自己的TimerTask,这个TimerTask可以被Timer调度。
内部的实现原理还是有点意思的,后面有时间来扒一扒它的实现原理。既然Timer这么简单为什么我不用呢?当然了,在这里我觉得Spring Task更简单。
三、Timer存在的问题
有一个显著问题是:Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务(task1和task2),且任务时间过长,超过了两个任务的间隔时间,那么就不再那么准时了。因为只有一个线程,线程需要排队,前面一个线程未及时执行完毕,势必会影响后续的任务执行。
第二个问题是:如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行。
如果不引入Spring如何解决上述问题呢?这个时候ScheduledExecutorService闪亮登场,利用线程池来调度任务,不会出现一个任务延迟导致第二个任务无法准时执行的问题,并且在ScheduledExecutorService调度两个任务的时候,其中一个任务抛出异常不影响第二个任务的正常执行。
具体的对比可以参见文章Java 并发专题 : Timer的缺陷 用ScheduledExecutorService替代
因此,尽量避免使用Timer要成为我们的共识啦。优秀的那么多,何必用这个呢?
当然,还有开源的定时器可以使用,功能更加强大,整合也不难。比如quartz和为分布式而生的Elastic-Job。