1) Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。 最早的时候就是这样写定时任务的。
2) 开源的第三方框架: Quartz 或者 elastic-job , 但是这个比较复杂和重量级,适用于分布式场景下的定时任务,可以根据需要多实例部署定时任务。
3) 使用Spring提供的注解: @Schedule 。 如果定时任务执行时间较短,并且比较单一,可以使用这个注解。
@Scheduled
串行方式
使用的注解:
@Scheduled
和@EnableScheduling
一般
@Slf4j
@Component
@EnableScheduling
public class ScheduleTask {
@Scheduled(cron = "0/2 * * * * ?")
public void task() throws InterruptedException {
log.info("执行定时任务1");
Thread.sleep(60000);
}
@Scheduled(cron = "0/2 * * * * ?")
public void task2() throws InterruptedException {
log.info("执行定时任务2");
Thread.sleep(5000);
}
}
@EnableScheduling添加在启动类中
结果
2018-12-21 15:25:56.000 INFO 4864 --- [ scheduling-1] : 执行定时任务1
2018-12-21 15:26:56.001 INFO 4864 --- [ scheduling-1] : 执行定时任务2
2018-12-21 15:27:01.002 INFO 4864 --- [ scheduling-1] : 执行定时任务1
2018-12-21 15:28:01.002 INFO 4864 --- [ scheduling-1] : 执行定时任务2
可以看出两个定时任务时互斥的,同一时间只有定时任务在执行。
并行方式
当定时任务很多的时候,为了提高任务执行效率,可以采用并行方式执行定时任务,任务之间互不影响, 只要实现SchedulingConfigurer接口就可以
@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(setTaskExecutors());
}
@Bean
public Executor setTaskExecutors(){
return Executors.newScheduledThreadPool(3); // 3个线程来处理。
}
}
结果
2018-12-21 15:34:02.002 INFO 2984 --- [pool-1-thread-1] : 执行定时任务1
2018-12-21 15:34:02.002 INFO 2984 --- [pool-1-thread-2] : 执行定时任务2
2018-12-21 15:34:08.002 INFO 2984 --- [pool-1-thread-2] : 执行定时任务2
2018-12-21 15:34:14.000 INFO 2984 --- [pool-1-thread-2] : 执行定时任务2
2018-12-21 15:34:20.001 INFO 2984 --- [pool-1-thread-3] : 执行定时任务2
2018-12-21 15:34:26.001 INFO 2984 --- [pool-1-thread-3] : 执行定时任务2
cron表达式
cron一共有7位,但是最后一位是年,可以留空,所以我们可以写6位
-
第一位,表示秒,取值0-59
-
第二位,表示分,取值0-59
-
第三位,表示小时,取值0-23
-
第四位,日期天/日,取值1-31
-
第五位,日期月份,取值1-12
-
第六位,星期,取值1-7,星期一,星期二...,注:不是第1周,第二周的意思 另外:1表示星期天,2表示星期一。
-
第7为,年份,可以留空,取值1970-2099
cron中,还有一些特殊的符号,含义如下
-
(*)星号:可以理解为每的意思,每秒,每分,每天,每月,每年...
-
(?)问号:问号只能出现在日期和星期这两个位置,表示这个位置的值不确定,每天3点执行,所以第六位星期的位置,我们是不需要关注的,就是不确定的值。同时:日期和星期是两个相互排斥的元素,通过问号来表明不指定值。比如,1月10日,比如是星期1,如果在星期的位置是另指定星期二,就前后冲突矛盾了。
-
(-)减号:表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12
-
(,)逗号:表达一个列表值,如在星期字段中使用“1,2,4”,则表示星期一,星期二,星期四
-
(/)斜杠:如:x/y,x是开始值,y是步长,比如在第一位(秒) 0/15就是,从0秒开始,每15秒,最后就是0,15,30,45,60 另:*/y,等同于0/y
实现定时任务的开启关闭以及定时时间可以配置
@RestController
@Slf4j
public class ScheduleController {
private final static Map<String,Task> taskMap = new HashMap<String,Task>();
@Autowired
private ThreadPoolTaskScheduler threadPoolTaskScheduler;
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
return new ThreadPoolTaskScheduler();
}
/**
* 创建定时任务
*/
@RequestMapping("/startTask/{id}")
public String start(@PathVariable("id") String id){
if(StringUtils.isNotEmpty(id) && !taskMap.containsKey(id)){
Task task = new Task(id);
ScheduledFuture<?> future = threadPoolTaskScheduler.schedule(task.getRunnable(),
new Trigger(){
public Date nextExecutionTime(TriggerContext triggerContext){
return new CronTrigger(task.getCron())
.nextExecutionTime(triggerContext);
}
}
);
task.setScheduledFuture(future);
taskMap.put(task.getTaskId(),task);
}
return "success";
}
/**
* 更新定时任务的cron
*/
@RequestMapping("/updateCron/{id}")
public String update(@RequestBody String cron,@PathVariable("id") String id){
if(taskMap.containsKey(id)){
Task task = taskMap.get(id);
ScheduledFuture<?> future = null;
try{
future = threadPoolTaskScheduler.schedule(task.getRunnable(),
new Trigger(){
public Date nextExecutionTime(TriggerContext triggerContext){
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
}
);
}catch (Exception e){
return "error";
}
task.getScheduledFuture().cancel(true);
task.setScheduledFuture(future);
}
return "success";
}
/**
* 删除定时任务
*/
@RequestMapping("/stopTask/{id}")
public String stop(@PathVariable("id") String id){
if(taskMap.containsKey(id)){
Task task = taskMap.get(id);
task.getScheduledFuture().cancel(true);
taskMap.remove(id);
}
return "success";
}
@Slf4j
@Data
static class Task{
private String taskId;
private Runnable runnable;
private ScheduledFuture scheduledFuture;
private String cron;
public Task(String taskId){
this.taskId = taskId;
runnable = ()->{
log.info("任务taskId:"+taskId);
};
cron = "0/5 * * * * ?";
}
}
}