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
。