1 Trigger
先对在TaskScheduler中将会使用到Trigger对象进行分析。 Trigger接口用于计算任务的下次执行时间。它的接口定义如下:
public interface Trigger {
/** * Determine the next execution time according to the given trigger context. * @param triggerContext context object encapsulating last execution times * and last completion time * @return the next execution time as defined by the trigger, * or {@code null} if the trigger won't fire anymore */
@Nullable
Date nextExecutionTime(TriggerContext triggerContext);
}
复制
可见它仅提供了一个接口:nextExecutionTime来获取下次执行时间。 这个方法接收的参数为TriggerContext对象,它能够获取上次任务原本的计划时间/实际的执行时间以及实际的完成时间。
Trigger接口的实现包含以下两个:
1.1 CronTrigger
它通过Cron表达式来生成调度计划。 如:
scheduler.schedule(task, new CronTrigger("0 15 9-17 * * MON-FRI"));
复制
以上表达式表示在工作日的9-17点之间,每隔15分钟执行一次; 关于Cron表达式的具体内容,在此不进行详述。
1.2 PeriodicTrigger
用于定期执行的Trigger;它有两种模式:
- fixedRate:两次任务开始时间之间间隔指定时长
- fixedDelay: 上一次任务的结束时间与下一次任务开始时间间隔指定时长
可见这两种情况的区别就在于,在决定下一次的执行计划时是否要考虑上次任务在什么时间执行完成。 默认情况下PeriodicTrigger使用了fixedDelay模式。 PeriodicTrigger提供以下参数来达成目的:
- period: long类型,表示间隔时长,注意在fixedRate与fixedDelay两种模式下的不同含义
- timeUnit: TimeUnit类型,表示间隔时长的单位,如毫秒等;默认是毫秒
- initialDelay: long类型,表示启动任务后间隔多长时间开始执行第一次任务
- fixedRate: boolean类型,表示是否是fixedRate,为True时是fixedRate,否则是fixedDelay,默认为False
2 TaskScheduler接口简介
2.1 接口简介
TaskScheduler用于对Runnable的任务进行调度,它包含有多种触发规则。默认的实现是ThreadPoolTaskScheduler。 它的主要包含的方法及说明如下:
public interface TaskScheduler {
/** * 提交任务调度请求 * * @param task 待执行任务 * @param trigger 使用Trigger指定任务调度规则 * @return */
ScheduledFuture schedule(Runnable task, Trigger trigger);
/** * 提交任务调度请求 * 注意任务只执行一次,使用startTime指定其启动时间 * * @param task 待执行任务 * @param startTime 任务启动时间 * @return */
ScheduledFuture schedule(Runnable task, Date startTime);
/** * 使用fixedRate的方式提交任务调度请求 * 任务首次启动时间由传入参数指定 * * @param task 待执行的任务 * @param startTime 任务启动时间 * @param period 两次任务启动时间之间的间隔时间,默认单位是毫秒 * @return */
ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
/** * 使用fixedRate的方式提交任务调度请求 * 任务首次启动时间未设置,任务池将会尽可能早的启动任务 * * @param task 待执行任务 * @param period 两次任务启动时间之间的间隔时间,默认单位是毫秒 * @return */
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
/** * 使用fixedDelay的方式提交任务调度请求 * 任务首次启动时间由传入参数指定 * * @param task 待执行任务 * @param startTime 任务启动时间 * @param delay 上一次任务结束时间与下一次任务开始时间的间隔时间,单位默认是毫秒 * @return */
ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
/** * 使用fixedDelay的方式提交任务调度请求 * 任务首次启动时间未设置,任务池将会尽可能早的启动任务 * * @param task 待执行任务 * @param delay 上一次任务结束时间与下一次任务开始时间的间隔时间,单位默认是毫秒 * @return */
ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
}
复制
2.2 TaskScheduler的实现类
2.2.1 TimerManagerTaskScheduler
用于包装CommonJ中的TimerManager接口。在使用CommonJ进行调度时使用。
2.2.2 ThreadPoolTaskScheduler
包装Java Concurrent中的ScheduledThreadPoolExecutor类,大多数场景下都使用它来进行任务调度。 除实现了TaskScheduler接口中的方法外,它还包含了一些对ScheduledThreadPoolExecutor进行操作的接口,其常用方法如下:
- setPoolSize 设置线程池大小,最小为1,默认情况下也为1;
- setErrorHandler 设置异常处理器。
- getScheduledThreadPoolExecutor 获取ScheduledExecutor,默认是ScheduledThreadPoolExecutor类型。
- getActiveCount 获取当前活动的线程数
- execute 提交执行一次的任务
- submit\submitListenable 提交执行一次的任务,并且返回一个Future对象供判断任务状态使用。
3 使用示例
Spring提供Scheduled注解来实现快捷的任务调度。本示例将使用该注解来说明其具体用法。
3.1 启用Scheduled注解支持
必须要使用@EnableScheduling注解来启用对@Scheduled注解的支持,@EnableScheduling必须使用在工程中某一个被Configuration注解的类上,如:
@Configuration
@EnableScheduling
public class MainConfiguration {
}
复制
3.2 Scheduled注解
Scheduled注解用在方法上,用于表示这个方法将会被调度。不同于Async注解,它所注解的方法返回类型最好是void类型的,否则它的返回值将不会被TaskScheduler所使用。同时,被它注解的方法不能有参数。如果要使用其它的对象的值,需要通过依赖注入的方式引用。 它包含有以下属性:
- cron: 使用Cron语法来指定调度计划
- zone: 指定时区,默认为本地时区
- fixedDelay: 指定fixedDelay的值,它表示上一次任务执行完后多长时间启动下一次任务,单位默认是毫秒
- fixedRate: 指定上一次任务开始时间到下一次任务开始时间的间隔时间,单位默认是毫秒
- initialDelay: 指定提交调度任务后多长时间开始执行第一次任务
其中,cron/fixedDelay/fixedRate三个属性必须且只能出现一个。 新建一个ScheduleService来测试其用途:
@Service
public class ScheduleService {
private static final Logger logger = LoggerFactory.getLogger(ScheduleService.class);
@Scheduled(fixedRate = 5000)
public void testSchedule() {
logger.info("TestSchedule begins to execute!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error("TestSchedule has been interrupted!", e);
return;
}
logger.info("TestSchedule execution was completed!");
}
}
复制
其执行结果如下:
2018-03-12 19:11:11.562 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule begins to execute!
2018-03-12 19:11:12.572 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule has been executed!
2018-03-12 19:11:16.557 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule begins to execute!
2018-03-12 19:11:17.560 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule has been executed!
2018-03-12 19:11:21.556 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule begins to execute!
2018-03-12 19:11:22.568 INFO 7788 --- [pool-2-thread-1] c.l.t.b.eshop.service.ScheduleService : TestSchedule has been executed!
复制
可以看到实际上使用是相当简单的,重点就在于fixedDelay与fixedRate两种方式的理解上,或者说是cron表达式的学习上。