文章
问答
冒泡
SpringBoot实现简单定时任务

前言

本文主要介绍两种定时任务的实现方式,一种是Spring内置的定时任务Spring Scheduler,还有一种是Spring Boot集成的Quartz。第一种方式应用起来比较简单,支持cron表达式和固定频率,但是无法监控任务状态,不支持动态调整,而Quartz框架是一个功能更为强大的调度器。

 

Spring Scheduler

Spring自带的定时任务有两种实现方式基于注解和基于接口,这两种形式都需要在启动类加上@EnableScheduling开启配置。

第一种:@Scheduled注解式

/**
 * cron表达式语法:[秒] [分] [小时] [日] [月] [周] [年]
 * @Scheduled(fixedDelay = 5000) //上一次执行完毕时间点之后5秒再执行
 * @Scheduled(fixedDelayString = "5000") //上一次执行完毕时间点之后5秒再执行
 * @Scheduled(fixedRate = 5000) //上一次开始执行时间点之后5秒再执行
 * @Scheduled(initialDelay=1000, fixedRate=5000) //第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
 * @Schedules 可以使用这个注解来指定多个@Scheduled规则
 */
@Component
public class DemoScheduled {

    @Scheduled(cron = "*/10 * * * * ?")
    public void doCron() {
        System.out.println(LocalDateTime.now() + "根据cron表达式执行定时任务");
    }

    @Scheduled(fixedRate = 1000 * 10)
    public void doRate() {
        System.out.println(LocalDateTime.now() + "根据上一次任务开始以后多久时间执行定时任务");
    }

    @Scheduled(fixedDelay = 1000 * 10)
    public void doDelay() {
        System.out.println(LocalDateTime.now() + "根据上一次任务结束以后多久时间执行定时任务");
    }

    @Scheduled(initialDelay = 1000 * 10, fixedDelay = 1000 * 10)
    public void doInitial() {
        System.out.println(LocalDateTime.now() + "首次延迟多长时间后执行");
    }

    @Schedules(value = {@Scheduled(cron = "*/20 * * * * ?"), @Scheduled(fixedDelay = 1000 * 15)})
    public void schedules() {
        System.out.println(LocalDateTime.now() + "多个定时配置");
    }
}

第二章基于SchedulingConfigurer接口实现

@Component
public class DemoSchedulingConfigurer implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 等同于@Scheduled(cron = "*/10 * * * * ?")
        taskRegistrar.addCronTask(()-> System.out.println(LocalDateTime.now() + "根据cron表达式执行定时任务"),
                "*/10 * * * * ?");

        // 等同于@Scheduled(fixedRate = 1000 * 10)
        taskRegistrar.addFixedRateTask(() -> System.out.println(LocalDateTime.now() + "根据上一次任务开始以后多久时间执行定时任务"),
                1000 * 10);


        // 等同于@Scheduled(fixedDelay = 1000 * 10)
        taskRegistrar.addFixedDelayTask(() -> System.out.println(LocalDateTime.now() + "根据上一次任务结束以后多久时间执行定时任务"),
                1000 * 10);
    }
}

 

这两种方式都是默认串行单线程执行,任务的执行时间会收到上一次任务执行时间的影响,如果要多线程执行需要单独配置线程池。

/**
 * 自定义线程池
 */
@Configuration
public class ThreadPoolConfig {
    @Bean("executor")
    public Executor getExecutor() {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10,
                20,
                60,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue(10000));
        return executor;
    }

}

基于注解多线程,通过@Async来实现多线程,需要在启动类加上@EnableAsync

    @Scheduled(cron = "* * * * * ?")
    @Async("executor")
    public void doCronByPool() {
        System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行1");
    }

    @Scheduled(fixedRate = 1000 * 1)
    @Async("executor")
    public void doRateByPool() {
        System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行2");
    }

基于接口多线程,通过ScheduledTaskRegistrar的setTaskScheduler来设置

   @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
       // 添加线程池
        taskRegistrar.setTaskScheduler(getTaskScheduler());

        taskRegistrar.addCronTask(() -> System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行1"),
                "* * * * * ?");
        taskRegistrar.addFixedRateTask(() -> System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + " 多线程执行2"),
                1000 * 1);
    }

    private ThreadPoolTaskScheduler getTaskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        taskScheduler.initialize();
        return taskScheduler;
    }

Spring Boot 整合 Quartz

spring boot项目中可以直接引入starter-quartz

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

先创建一个任务类用于任务调度后具体处理操作

/**
* 继承spring封装的QuartzJobBean
*/
public class DemoJob extends QuartzJobBean {
	@Override
	protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
		System.out.println(LocalDateTime.now() + "DemoJob");
	}
}

然后写一个配置类将任务类与触发时间绑定

/**
 * Quartz的相关配置,注册JobDetail和Trigger
 * 注意JobDetail和Trigger是org.quartz包下的,不是spring包下的,不要导入错误
 */
@Configuration
public class DemoJobConfig {

    @Bean
    public JobDetail demoJobDetail() {
        JobDetail jobDetail = JobBuilder.newJob(DemoJob.class)
                .withIdentity("demoJob")
                .storeDurably()
                .build();
        return jobDetail;
    }

    @Bean
    public Trigger demoTrigger() {

        // 间隔10秒
        SimpleScheduleBuilder simpleSchedule = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10).repeatForever();
        // cron表达式
//        CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("*/10 * * * * ?");

        Trigger trigger = TriggerBuilder.newTrigger()
                .forJob(demoJobDetail())
                .withIdentity("demoJob")
                .withSchedule(simpleSchedule)
                .startNow()
                .build();

        return trigger;
    }
}

 

参考文档

https://github.com/quartz-scheduler/quartz/blob/master/docs/quick-start-guide.adoc#starting-a-sample-application

https://docs.spring.io/spring-boot/docs/2.6.3/reference/html/io.html#io.quartz


关于作者

TimothyC
天不造人上之人,亦不造人下之人
获得点赞
文章被阅读