问题
@Scheduled默认为单线程,开启多个任务时,任务的执行时机会受上一个任务执行时间的影响。
@Component
public class ScheduledTasks {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Scheduled(fixedDelay = 1000)
public void first() throws InterruptedException {
System.out.println("第一个定时任务开始 : " + sdf.format(new Date()) + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
Thread.sleep(1000 * 5); //模拟第一个任务执行的时间
}
@Scheduled(fixedDelay = 1000)
public void second() {
System.out.println("第二个定时任务开始 : " + sdf.format(new Date()) + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
}
解决方法
在task上加@EnableAsync注解,在任务方法上加@Async注解
@Component
@EnableAsync
public class ScheduledTasks {
private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm;ss");
@Async
@Scheduled(fixedDelay = 1000)
public void first() throws InterruptedException {
System.out.println("第一个定时任务开始 : " + sdf.format(new Date()) + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
Thread.sleep(1000 * 5);
}
@Async
@Scheduled(fixedDelay = 1000)
public void second() {
System.out.println("第二个定时任务开始 : " + sdf.format(new Date()) + "\r\n线程 : " + Thread.currentThread().getName());
System.out.println();
}
}
另外,由于开启了多线程,第一个任务的执行时机也不受其本身执行时间的限制,需要注意数据幂等性。
解决方法2
/**
* @author YuanChong
* @create 2019-06-20 14:39
* @desc
*/
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Autowired
private Executor executor;
private final ThreadFactory threadFactory = new BasicThreadFactory.Builder().namingPattern("hualala-scheduled-task-%d").build();
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(executor);
}
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(8, threadFactory);
}
}
这种方式的好处在于单任务自己是串行,任务与任务之间是并行。
@Scheduled属性
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
String cron() default "";
String zone() default "";
long fixedDelay() default -1L;
String fixedDelayString() default "";
long fixedRate() default -1L;
String fixedRateString() default "";
long initialDelay() default -1L;
String initialDelayString() default "";
}
单线程下:
- cron每一次执行完成后,再遇到给定的规则时间点上触发。
- fixedDelay受上一次执行时间的影响,再间隔设置时间后执行。
- fixedRate安装固定的速率执行,如果一段时间内任务阻塞,会积攒任务次数,在恢复时一次性执行完成。
另,initialDelay属性可同时配合上面属性使用,用于初次执行前等待的时间。