前言
关于相关定时任务的开发工具一般用到
quartz
与Spring Task
两种方式。
关于quartz
的配置相对要复杂一些,具体请看我之前的认识quartz定时器。
而Spring Task
更为广泛使用,非常轻量级配置简单,使用方便。
一、相关配置
在spring
的配置文件applicationContext.xml
中配置。
1、在xmlns
中添加:
xmlns:task="http://www.springframework.org/schema/task"
2、在xsi:schemaLocation
中添加:
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.1.xsd
3、增加注解驱动及扫描包配置
<task:annotation-driven/>
<!--扫描的是定时任务存放的包位置-->
<context:component-scan base-package="com.how2java.controller.base" />
二、实现简单定时任务
1、在确保扫描包能扫描能正确扫描的前提下,在扫描包下创建interface TaskConfig
接口:
public interface TaskConfig {
public void task();
}
2、实现接口任务TaskAconfig
与TaskBconfig
//@EnableScheduling //该注解功能可以替代spring中<task:annotation-driven/>驱动配置
@Component
public class TaskAConfig implements TaskConfig{
private static final Logger logger = LogManager.getLogger(TaskAConfig.class);
@Scheduled(cron="0/5 * * * * ?") //每5m执行一次
public void task(){
logger.info("任务A执行......");
}
}
//@EnableScheduling //该注解功能可以替代spring中<task:annotation-driven/>驱动配置
@Component
public class TaskBConfig implements TaskConfig{
private static final Logger logger = LogManager.getLogger(TaskBConfig.class);
@Scheduled(cron="0/10 * * * * ?") //每10m执行一次
public void task(){
logger.info("任务B执行......");
}
}
- 定时任务方法中不能有返回值,即
public void task()
不能有返回值,如果需要查看返回值,可以利用日志来进行查看。 @EnableScheduling
注解:可以代替该注解功能可以替代spring
配置文件中<task:annotation-driven/>
任务驱动配置。也就是不需要在applicationContext.xml
中添加<task:annotation-driven/>
cron
的时间表达式一般有至少6个(可能也会7个)有空格分隔的时间元素。
1.秒(0~59)
2.分钟(0~59)
3.小时(0~23)
4.天(月)(0~31,但是你需要考虑你月的天数)
5.月(0~11)
6.天(星期)(1~7 或SUN ,MON,TUE,WED,THU,FRI,SAT)
7.年份(1970-2099)
每个元素可以是:
一个值(如6)
一个连续区间(9-12)
一个间隔时间(8-18/4)(/表示每隔4小时)
一个列表(1,3,5)
通配符:由于”月份中的日期”和”星期中的日期”这两个元素互斥的,必须要对其中一个设置?.
举例:
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 15 10 * * ? 每天上午10:15触发
0 15 10 ? * 6L 每月的最后一个星期五上午10:15触发
0 15 10 ? * 6#3 每月的第三个星期五上午10:15触发
0 10,44 14 ? 3 WED 每年三月的星期三的下午2:10和2:44触发
- 在
applicationContext.xml
中没有配置线程池的话,定时任务默认的是单线程。
如图,任务AB都执行,都属于默认的线程pool-1-thread-1
三、实现多线程定时任务
由上可知,定时任务默认的单线程。对于简单的业务来说是没问题的,但是要实现多线程下较复杂的定时任务就需要配置多线程任务,其配置过程也十分简单。
首先,我们先看下默认的单线程定时任务:
业务要求:
任务A 5m 执行一次,但是sleep拖后10m执行即15m 执行一次,
任务B 10m 执行一次
@Component
public class TaskAConfig implements TaskConfig{
private static final Logger logger = LogManager.getLogger(TaskAConfig.class);
@Scheduled(cron="0/5 * * * * ?") //每5m执行一次
public void task(){
try {
Thread.sleep(10000);//我们的业务是要求任务A先sleep 10m之后执行
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("任务A执行......");
}
}
@Component
public class TaskBConfig implements TaskConfig{
private static final Logger logger = LogManager.getLogger(TaskBConfig.class);
@Scheduled(cron="0/10 * * * * ?") //每10m执行一次
public void task(){
logger.info("任务B执行......");
}
}
但是执行的结果是明明任务B每隔10m执行,现在变成了跟任务A一样15m执行一次,被拖后了5m,这样是不符合业务需求的。
接下里是多线程的实现:
在applicationContext.xml
配置线程池即可完美实现多线程
<task:annotation-driven scheduler="myScheduler"/>
<!-- 配置定时任务线程池 -->
<task:scheduler id="myScheduler" pool-size="5"/>
执行结果如图,任务B不会被拖后,即任务A与任务B不再属于同一个线程,而是在线程池中不同的线程。