目录
- SpringBoot定时任务@Scheduler
- 重要一点
- 总结
SpringBoot定时任务@Scheduler
Spring Boot 中一般来说定时任务使用@Scheduler注解或者Quartz框架,或者自定义线程实现,最简单的当属@Scheduler注解
- 依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.1.3.RELEASE</version> </dependency>
- 创建Application:
@SpringBootApplication //此注解开启异步定时任务,必须要有 @EnableScheduling public class Appliandroidcation { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
- 创建任务:
@Component public class SpringJob { //springboot扫描到这个注解就会按照定义的计划去执行任务 //cron是cron表达式,因为很简单但php是描述要话费比较多的篇幅,所以略 @Scheduled(cron = "0/1 * * * * ?") public void myJob(){ //任务处理逻辑 System.out.println(new Date() +"Spring Scheduler 执行"); } }
- 启动 Application:
上述是一个最简单的单job cron调度的实现.
- @Scheduler中的参数:
String cron() default ""; String zone() default ""; long fixedDelay() default -1; String fixedDelayString() default ""; long fixedRate() default -1; String fixedRateString() default ""; long initialDelay() default -1; String initialDelayString() default "";
String cron() default “”;接收一个cron表达式字符串,以cron表达式调度任务 String zone() default “”;设置时区,不设置的话cron就基于服务器所在的本地时区 long fixedDelay() default -1;设置任务间隔时间(毫秒),就是任务结束后再过多久开始新的任务 String fixedDelayString() default “”;同 long fixedDelay(),只是把参数从long改成string long fixedRate() default -1;设置任务每隔多久开启 String fixedRateString() default “”;同 long fixedRate() 只是把参数从long改成string long initialDelay() default -1;第一次执行延迟时间(启动后多久才开始任务) String initialDelayString() default “”;同 long initialDelay()
- spring boot 默认开启一个线程执行任务:
@Component public class SpringJob { @Scheduled(fixedRateString = "1000") public void myJob2() throws InterruptedException { System.out.println(new Date() +" job开启"); Thread.sleep(2000); System.out.println(new Date() +" job结束"); } } 执行结果: Fri Jul 31 10:34:16 CST 2020 job开启 Fri Jul 31 10:34:18 CST 2020 job结束 Fri Jul 31 10:34:18 CST 2020 job开启 Fri Jul 31 10:34:20 CST 2020 job结束 Fri Jul 31 10:34:20 CST 2020 job开启 Fri Jul 31 10:34:22 CST 2020 job结束 Fri Jul 31 10:34:22 CST 2020 job开启 Fri Jul 31 10:34:24 CST 2020 job结束 Fri Jul 31 10:34:24 CST 2020 job开启 Fri Jul 31 10:34:26 CST 2020 job结束 Fri Jul 31 10:34:26 CST 2020 job开启 Fri Jul 31 10:34:28 CST 2020 job结束 Fri Jul android31 10:34:28 CST 2020 job开启 Fri Jul 31 10:34:30 CST 2020 job结束
设置的任务为每隔1秒中开启一次,一次任务执行时间是2秒,但是发现执行的过程是,同一时间只有一个线程在执行.也就是单线程执行
- 现在执行两个job,单线程情况下,自然也是无法并发的:
@Component public class SpringJob { @Scheduled(fixedRateString = "2000") public void myJob() throws InterruptedException { System.out.println(new Date() +" job1开始"); Thread.sleep(10000); System.out.println(new Date() +" job1结束"); } @Scheduled(fixedRateString = "1000") public void myJob2() throws InterruptedException { System.out.println(new Date() +" job2开启"); Thread.sleep(2000); System.out.println(new Date() +" job2结束"); } } 执行结果: Fri Jul 31 10:51:56 CST 2020 job1开始 Fri Jul 31 10:52:06 CST 2020 job1结束 Fri Jul 31 10:52:06 CST 2020 job2开启 Fri Jul 31 10:52:08 CST 2020 job2结束 Fri Jul 31 10:52:08 CST 2020 job2开启 Fri Jul 31 10:52:10 CST 2020 job2结束 Fri Jul 31 10:52:10 CST 2020 job1开始 Fri Jul 31 10:52:20 CST 2020 job1结束 Fri Jul 31 10:52:20 CST 2020 job2开启 Fri Jul 31 10:52:22 CST 2020 job2结束 Fri Jul 31 10:52:22 CST 2020 job2开启 Fri Jul 31 10:52:24 CST 2020 job2结束
任务的每次执行,无论是单个任务多次执行,还是多个任务一次执行,对于线程来说都是一样的,都需要开启一个线程.有几个线程就可以几个任务(或者相同任务的多次)并发执行
默认情况下,scheduler只有一个线程
- 自定义线程池,添加一个配置类:
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(setExecutor()); } @Bean public Executor setExecutor() { //设置线程个数,这里我们设置5个(同一时间可以并行执行5个线程) return Executors.newScheduledThreadPool(5); } }
- 此时执行上方的单个任务:
Fri Jul 31 10:55:18 CST 2020 job开启
Fri Jul 31 10:55:20 CST 2020 job结束Fri Jul 31 10:55:20 CST 2020 job开启Fri Jul 31 10:55:22 CST 2020 job结束Fri Jul 31 10:55:22 CST 2020 job开启Fri Jul 31 10:55:24 CST 2020 job结束Fri Jul 31 10:55:24 CST 2020 job开启Fri Jul 31 10:55:26 CST 2020 job结束
单个任务还是只能串行执行.
- 执js行上方的两个任务:
Fri Jul 31 10:59:59 CST 2020 job2开启
Fri Jul 31 10:59:59 CST 2020 job1开始Fri Jul 31 11:00:01 CST 2020 job2结束Fri Jul 31 11:00:01 CST 2020 job2开启Fri Jul 31 11:00:03 CST 2020 job2结束Fri Jul 31 11:00:03 CST 2020 job2开启Fri Jul 31 11:00:05 CST 2020 job2结束Fri Jul 31 11:00:05 CST 2020 job2开启Fri Jul 31 11:00:07 CST 202编程客栈0 job2结束Fri Jul 31 11:00:07 CST 2020 job2开启Fri Jul 31 11:00:09 CST 2020 job1结束Fri Jul 31 11:00:09 CST 2020 job1开始Fri Jul 31 11:00:09 CST 2020 job2结束
发现任务跟job1与job2之间是可以并发执行的.但是对于job1的多次任务,job2的多次任务,都是串行执行的.
现在线程池中有多个线程,job与job之间可以并发,如何让单个job的多次可以并发执行,就需要@Async注解,此注解要用在job的方法上.@EnableAsync注解,此注解要在Application类上,此时,job的多次执行也可以并发执行了.
@SpringBootApplication @EnableScheduling @EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } @Component public class SpringJob { @Async @Scheduled(fixedRateString = "2000") public void myJob() throws InterruptedException { System.out.println(new Date() + " job1开始"); Thread.sleep(10000); System.out.println(new Date() + " job1结束"); } @Scheduled(fixedRateString = "1000") public void myJob2() throws InterruptedException { System.out.println(new Date() + " job2开启"); Thread.sleep(2000); System.out.println(new Date() + " job2结束"); } } 执行结果: Fri Jul 31 11:08:57 CST 2020 job2开启 Fri Jul 31 11:08:57 CST 2020 job1开始 Fri Jul 31 11:08:59 CST 2020 job1开始 Fri Jul 31 11:08:59 CST 2020 job2结束 Fri Jul 31 11:08:59 CST 2020 job2开启 Fri Jul 31 11:09:01 CST 2020 job1开始 Fri Jul 31 11:09:01 CST 2020 job2结束 Fri Jul 31 11:09:01 CST 2020 job2开启 Fri Jul 31 11:09:03 CST 2020 job1开始 Fri Jul 31 11:09:03 CST 2020 job2结束 Fri Jul 31 11:09:03 CST 2020 job2开启
可以发现job1在前一次没有执行完时,就已经开始执行了.
重要一点
自定线程池Configuration是为了启用多线程,此时可以起到job1与job2之间的并发执行,但是job1多次任务只能串行执行,所以才增加了@Aysnc跟@EnableAsync注解.
@EnableAsync注解有两个作用,1单线程变多线程,2 可以允许单个job多次运行时并行执行(需要再加@Async),如果已经是多线程了,但是不加此注解,同一个job的多次执行还是串行.
其实在使用@EnableAsync注解的时候,springboot会把自定义的configuration给覆盖掉,即使在configuration中配置了单个线程,任务的并发也不会受到影响.所以推荐使用@EnableAsync来完成多线程配置再加@Aysnc来完成任务的并发.
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程客栈(www.devze.com)。
精彩评论