文章目录
- WHAT
- WHY
- HOW
- 扩展
- 原理
- 参考
WHAT
@Scheduled 由Spring定义,用于将方法设置为调度任务。如:方法每隔十秒钟被执行、方法在固定时间点被执行等
- @Scheduled(fixedDelay = 1000)
上一个任务结束到下一个任务开始的时间间隔为固定的1秒,任务的执行总是要先等到上一个任务的执行结束 - @Scheduled(fixedRate = 1000)
每间隔1秒钟就会执行任务(如果任务执行的时间超过1秒,则下一个任务在上一个任务结束之后立即执行) - @Scheduled(fixedDelay = 1000, initialDelay = 2000)
第一次执行的任务将会延迟2秒钟后才会启动 - @Scheduled(cron = “0 15 10 15 * ?”)
Cron表达式,每个月的15号上午10点15开始执行任务 - 在配置文件中配置任务调度的参数
@Scheduled(fixedDelayString = “{fixedRate.in.milliseconds}”)
@Scheduled(cron = “${cron.expression}”)
WHY
@Scheduled使用方便,不需要自己去写复杂的任务调度。注解的方式非常灵活,只需要在方法上添加@Scheduled注解就能定义调度的任务,任何无参的方法都可以瞬间成为供调度的任务
/**
* 关闭线程池中超过空闲时间的线程
* @param poolingConnectionManager
* @return
*/
@Bean
public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager poolingConnectionManager) {
return new Runnable() {
@Override
@Scheduled(fixedDelay = 10000, initialDelay = 1000)
public void run() {
try {
if (poolingConnectionManager != null) {
log.info("run IdleConnectionMonitor - Closing expired and idle connections...");
poolingConnectionManager.closeExpiredConnections();
poolingConnectionManager.closeIdleConnections(properties.getCloseIdleConnectionWaitTimeSecs(), TimeUnit.SECONDS);
} else {
log.info("run IdleConnectionMonitor - Http Client Connection manager is not initialised");
}
} catch (Exception e) {
log.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e);
}
}
};
}
HOW
Spring是如何实现如此简洁的任务调度定义的?怎么使用@Scheduled?
- Requirements
- Spring容器中需要创建 Bean: ScheduledAnnotationBeanPostProcessor
- Spring容器中需要有TaskScheduler实现类的实例
- 无参方法添加@Scheduled注解
扩展
扩展有两种方式
- 实现SchedulingConfigurer接口
// 优先级高
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(threadPoolTaskScheduler());
}
@Bean(destroyMethod = "shutdown")
public ThreadPoolTaskScheduler threadPoolTaskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("poolmary&mark");
scheduler.setPoolSize(50);
return scheduler;
}
}
- 创建TaskScheduler的实现Bean
// 相对实现SchedulingConfigurer接口的方式,优先级低
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setThreadNamePrefix("poolScheduler");
scheduler.setPoolSize(50);
return scheduler;
}
原理
- ScheduledAnnotationBeanPostProcessor的postProcessAfterInitialization方法将@Scheduled的方法包装为指定的task添加到ScheduledTaskRegistrar中
- ScheduledAnnotationBeanPostProcessor会监听Spring的容器初始化事件,在Spring容器初始化完成后进行TaskScheduler实现类实例的查找,若发现有SchedulingConfigurer的实现类实例,则跳过3
- 查找TaskScheduler的实现类实例默认是通过类型查找,若有多个实现则会查找名字为"taskScheduler"的实现Bean,若没有找到则在 ScheduledTaskRegistrar调度任务的时候会创建一个newSingleThreadScheduledExecutor,将TaskScheduler的实现类实例设置到ScheduledTaskRegistrar属性中
- ScheduledTaskRegistrar的scheduleTasks方法触发任务调度
- 真正调度任务的类是TaskScheduler实现类中的ScheduledExecutorService,由J.U.C提供