文章目录

  • WHAT
  • WHY
  • HOW
  • 扩展
  • 原理
  • 参考


WHAT

@Scheduled 由Spring定义,用于将方法设置为调度任务。如:方法每隔十秒钟被执行、方法在固定时间点被执行等

  1. @Scheduled(fixedDelay = 1000)
    上一个任务结束到下一个任务开始的时间间隔为固定的1秒,任务的执行总是要先等到上一个任务的执行结束
  2. @Scheduled(fixedRate = 1000)
    每间隔1秒钟就会执行任务(如果任务执行的时间超过1秒,则下一个任务在上一个任务结束之后立即执行)
  3. @Scheduled(fixedDelay = 1000, initialDelay = 2000)
    第一次执行的任务将会延迟2秒钟后才会启动
  4. @Scheduled(cron = “0 15 10 15 * ?”)
    Cron表达式,每个月的15号上午10点15开始执行任务
  5. 在配置文件中配置任务调度的参数

@Scheduled(fixedDelayString = “springboot handlerinterceptor生效 springboot@scheduled_任务调度{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
  1. Spring容器中需要创建 Bean: ScheduledAnnotationBeanPostProcessor
  2. Spring容器中需要有TaskScheduler实现类的实例
  3. 无参方法添加@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;
    }

原理

  1. ScheduledAnnotationBeanPostProcessorpostProcessAfterInitialization方法将@Scheduled的方法包装为指定的task添加到ScheduledTaskRegistrar
  2. ScheduledAnnotationBeanPostProcessor会监听Spring的容器初始化事件,在Spring容器初始化完成后进行TaskScheduler实现类实例的查找,若发现有SchedulingConfigurer的实现类实例,则跳过3
  3. 查找TaskScheduler的实现类实例默认是通过类型查找,若有多个实现则会查找名字为"taskScheduler"的实现Bean,若没有找到则在 ScheduledTaskRegistrar调度任务的时候会创建一个newSingleThreadScheduledExecutor,将TaskScheduler的实现类实例设置到ScheduledTaskRegistrar属性中
  4. ScheduledTaskRegistrarscheduleTasks方法触发任务调度
  5. 真正调度任务的类是TaskScheduler实现类中的ScheduledExecutorService,由J.U.C提供