spring Task是spring boot中一个简单方便的定时任务

一:

在启动类Application.java中类上面加上@EnableScheduling开启定时任务,也可以启用异步:@EnableAsync


@EnableScheduling  // 开启定时任务
@EnableAsync  //开启异步
public class Application {
 
    public static void main(String[] args) {
        // 程序启动入口
        // 启动嵌入式的 Tomcat 并初始化 Spring 环境及其各 Spring 组件
        SpringApplication.run(Application.class,args);
    }
}

二:

在controller层需要启用定时任务的方法上面加上@Scheduled注释


//@Scheduled(cron = "0/9 * * * * ? ")//定时9秒钟执行一次
//@Scheduled(cron = "0 5 14 * * ? ")//定时每天14:05执行
@Scheduled(cron = "0 0 9 * * ? ")//定时每天9点执行
public void testTask() throws Exception{
	System.out.println("执行定时任务");
	timingTaskService.timingTask();
}

@Scheduled注解

  • @Scheduled(fixedRate = 5000) :上一次执行开始时间点之后5秒再执行
  • @Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
  • @Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次。initialDelay表示第一次调用前的延时,单位毫秒,必需配合cron、fixedDelay或者fixedRate来使用。
  • @Scheduled(cron="*/5 * * * * *") :通过cron表达式定义规则,与fixedDelay类似,上次执行完毕后才进行下次调度。

定时任务串行执行的优先级

定义两个具有相同cron表达式的定时任务task01和task02,都是以固定时间间隔每1秒打印一次,看他们的输出是否会乱掉,如果每次都是任务1打印完再打印任务2,那就是固定优先级的,否则每次调度顺序是随机的。


	@Scheduled(cron = "0/1 * * * * ?")
	public void task01() throws InterruptedException {
		System.out.println(Thread.currentThread().getName() + " | task01 " + new Date().toLocaleString());
	}
 
	@Scheduled(cron = "0/1 * * * * ?")
	public void task02() {
		System.out.println(Thread.currentThread().getName() + " | task02 " + new Date().toLocaleString());
	}

动态创建定时任务

使用注解的方式,无法实现动态的修改、添加或关闭定时任务,这个时候就需要使用编程的方式进行任务的更新操作了。可直接使用ThreadPoolTaskScheduler或者SchedulingConfigurer接口进行自定义定时任务创建。

1.SchedulingConfigurer

实现SchedulingConfigurer接口,重写configureTasks方法时添加定时任务:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
 
	@Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
		taskRegistrar.setTaskScheduler(taskScheduler());
		
		taskRegistrar.getScheduler().schedule(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "|SchedulingConfigurer cron task01");
			}
		}, new CronTrigger("0/3 * * * * ?"));
		
		taskRegistrar.addCronTask(new CronTask(new Runnable() {
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName() + "|SchedulingConfigurer cron task02");
			}
		}, new CronTrigger("0/2 * * * * ?")));
	}
 
	@Bean
	public TaskScheduler taskScheduler() {
		ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
		taskScheduler.setPoolSize(10);
		taskScheduler.setThreadNamePrefix("spring-task-scheduler-thread-");
		taskScheduler.setAwaitTerminationSeconds(60);
		taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
		taskScheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
		return taskScheduler;
	}
 
}

2.ThreadPoolTaskScheduler

ThreadPoolTaskScheduler是Spring Task的核心实现类,该类提供了大量的重载方法进行任务调度。首先配置一个自定义任务调度线程池ThreadPoolTaskScheduler,可参考上面ScheduleConfig中的配置,这里不再赘述。

编写一个控制层实现定时任务的动态创建、关闭以及修改:


@Slf4j
@RestController
@RequestMapping("/task")
public class DynamicTaskController {
 
	@Resource
	private ThreadPoolTaskScheduler taskScheduler;
 
	private ScheduledFuture<?> scheduledFuture;
 
	@Value("${timing-task.cron1}")
	private String cronStr1;
 
	@Value("${timing-task.cron2}")
	private String cronStr2;
 
	@RequestMapping("/start")
	public String startTask() {
		scheduledFuture = taskScheduler.schedule(new RunTask01(), new Trigger() {
			@Override
			public Date nextExecutionTime(TriggerContext triggerContext) {
				return new CronTrigger(cronStr1).nextExecutionTime(triggerContext);
			}
		});
		log.info("start timed task success ..");
		return "start task suceess";
	}
 
	@RequestMapping("/stop")
	public String stopTask() {
		Boolean result = null;
		if (scheduledFuture != null) {
			result = scheduledFuture.cancel(true);
		}
		log.info("stop timed task result: " + result);
		return "stop task result: " + result;
	}
 
	@RequestMapping("/modify")
	public String modifyTask() {
		Boolean stopResult = null;
		// 停止定时任务
		if (scheduledFuture != null) {
			stopResult = scheduledFuture.cancel(true);
		} else {
			log.info("modify task error -> scheduledFuture is null");
			return "error";
		}
		// 更换cron重新开启定时任务
		if (stopResult) {
			scheduledFuture = taskScheduler.schedule(new RunTask01(), new Trigger() {
				@Override
				public Date nextExecutionTime(TriggerContext triggerContext) {
					return new CronTrigger(cronStr2).nextExecutionTime(triggerContext);
				}
			});
			log.info("modify task success ..");
			return "success";
		}
		log.info("modify task failed ..");
		return "failed";
	}
 
	class RunTask01 implements Runnable {
 
		@Override
		public void run() {
			log.info(Thread.currentThread().getName() + "|schedule task01" + "|" + new Date().toLocaleString());
		}
	}
 
	class RunTask02 implements Runnable {
		@Override
		public void run() {
			log.info(Thread.currentThread().getName() + "|schedule task02" + "|" + new Date().toLocaleString());
		}
	}
 
}

3.配置文件中定义cron表达式:

timing-task.cron1=0/1 * * * * ?
timing-task.cron2=0/5 * * * * ?

通过这种编程式的定时任务优点是灵活可拓展,缺点是不能与异步调用相结合,例如@Async注解。

ThreadPoolTaskScheduler 除了提供了schedule()还有其他方法设置定时任务,简单示例如下:

@RequestMapping("/test")
public String test(){
	Date startTime = new Date();// 起始时间点
	long period = 1000, delay = 2000;// 时间间隔,毫秒
	// 从指定时间点开始以固定频率执行定时任务,类似于@Scheduled(fixedRate = 1000, initialDelay=0)
	taskScheduler.scheduleAtFixedRate(new RunTask01(), startTime, period);
	// 从指定时间点开始以固定时间间隔执行定时任务,类似于@Scheduled(fixedDelay = 2000, initialDelay=0)
	taskScheduler.scheduleWithFixedDelay(new RunTask02(), startTime, delay);
	return "done";
}