SpringBoot实现定时任务
定时任务实现的几种方式:
- Timer:这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
- ScheduledExecutorService:也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。
- Spring Task:Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。
- Quartz:这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。
使用Spring Task
1.在SpringBoot项目中,我们可以很优雅的使用注解来实现定时任务,首先创建项目,导入依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--Spring Task依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.创建任务类:
@Slf4j
@Component
public class ScheduledService {
//5s执行一次
@Scheduled(cron = "0/5 * * * * *")
public void scheduled(){
log.info("=====>>>>>使用cron {}",System.currentTimeMillis());
}
//5s执行一次
@Scheduled(fixedRate = 5000)
public void scheduled1() {
log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis());
}
//5s执行一次
@Scheduled(fixedDelay = 5000)
public void scheduled2() {
log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());
}
}
3.在主类上使用@EnableScheduling注解开启对定时任务的支持,然后启动项目创建任务类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class SpringBootScheduledApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootScheduledApplication.class, args);
}
}
__运行结果:__可以看到三个定时任务都已经执行,并且使同一个线程中串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。
4.**增加多线程:**在传统的Spring项目中,我们可以在xml配置文件添加task的配置,而在SpringBoot项目中一般使用config配置类的方式添加配置,所以新建一个AsyncConfig类
@Configuration
@EnableAsync
public class AsyncConfig {
/*
此处成员变量应该使用@Value从配置中读取
*/
private int corePoolSize = 10;
private int maxPoolSize = 200;
private int queueCapacity = 10;
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.initialize();
return executor;
}
@Configuration
:表明该类是一个配置类
@EnableAsync
:开启异步事件的支持
然后在定时任务的类或者方法上添加
@Async
。最后重启项目,每一个任务都是在不同的线程中
执行时间配置
在上面的定时任务中,我们在方法上使用@Scheduled注解来设置任务的执行时间,并且使用三种属性配置方式:
- fixedRate:定义一个按一定频率执行的定时任务
- fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,改属性可以配合initialDelay, 定义该任务延迟执行时间。
- cron:通过表达式来配置任务执行时间
@Scheduled(cron = “* * * * * *”)
1. cron
表达式格式:
{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
“*”
字符代表所有可能的值。“*”
在{月份}
里表示每个月的含义。“/”
字符用来指定数值的增量。
在{分钟}
里的“0/15”
表示从第0分钟开始,每15分钟。
在{分钟}
里的“3/20”
表示从第3分钟开始,每20分钟(它和“3,23,43”)的含义一样。“L”
字符仅被用于{日期}
和{星期}
,它是单词“last”
的缩写。
在{日期}
,“L”表示一个月的最后一天。
在{星期}
,“L”表示一个星期的最后一天,也就是SAT
。- 如果在
“L”
前有具体的内容,它就具有其他的含义了。
“6L”
表示这个月的倒数第6天,“FRIL”
表示这个月的最一个星期五。
注意:在使用“L”参数时,不要指定列表或范围, - 由于
{日期}
和{星期}
这两个元素互斥的, 其中之一被指定了值以后, 必须要对另一个设为”?”
。
2. cron
表达式各占位符解释:
-
{秒数}{分钟} ==> 允许值范围: 0~59 ,不允许为空值。
-
“*”
代表每隔1秒钟触发。 -
“,”
代表在指定的秒数触发。比如”0,15,45”
代表0秒、15秒和45秒
时触发任务。 -
“-“
代表在指定的范围内触发,比如”25-45”
代表从25秒开始触发到45秒结束
触发,每隔1秒触发1次。 -
{小时} ==> 允许值范围: 0~23 ,不允许为空值,若值不合法。占位符和秒数一样。
-
{日期} ==> 允许值范围: 1~31 ,不允许为空值。
-
{月份} ==> 允许值范围: 0~11
-
{星期} ==> 允许值范围: 1~7 (或 SUN,MON,TUE,WED,THU,FRI,SAT), 1代表星期天(SUN),以此类推,7代表星期六(SAT),不允许为空值。
-
{年份} ==> 允许值范围: 1970~2099 ,允许为空。
3. 经典案例
“30 * * * * ?”
每半分钟触发任务
“30 10 * * * ?”
每小时的10分30秒触发任务
“30 10 1 * * ?”
每天1点10分30秒触发任务
“0 0 10,14,16 * * ?”
每天上午10点,下午2点,4点
“0 0/30 9-17 * * ?”
朝九晚五工作时间内每半小时
“0 * 14 * * ?”
在每天下午2点到下午2:59期间的每1分钟触发
“0 0/5 14 * * ?”
在每天下午2点到下午2:55期间的每5分钟触发
“0 0/5 14,18 * * ?”
在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
“0 0-5 14 * * ?”
在每天下午2点到下午2:05期间的每1分钟触发
“30 10 1 20 * ?”
每月20号1点10分30秒触发任务
“30 10 1 20 10 ? *”
每年10月20号1点10分30秒触发任务
“30 10 1 20 10 ? 2011”
2011年10月20号1点10分30秒触发任务
“30 10 1 ? 10 * 2011”
2011年10月每天1点10分30秒触发任务
“30 10 1 ? 10 SUN 2011”
2011年10月每周日1点10分30秒触发任务
“15,30,45 * * * * ?”
每15秒,30秒,45秒时触发任务
“15-45 * * * * ?”
15到45秒内,每秒都触发任务
“15/5 * * * * ?”
每分钟的每15秒开始触发,每隔5秒触发一次
“15-30/5 * * * * ?”
每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
“0 0/3 * * * ?”
每小时的第0分0秒开始,每三分钟触发一次
“0 15 10 ? * MON-FRI”
星期一到星期五的10点15分0秒触发任务
“0 15 10 L * ?”
每个月最后一天的10点15分0秒触发任务
“0 15 10 LW * ?”
每个月最后一个工作日的10点15分0秒触发任务
“0 15 10 ? * 5L”
每个月最后一个星期四的10点15分0秒触发任务
“0 15 10 ? * 5#3”
每个月第三周的星期四的10点15分0秒触发任务
整合Quartz
1.添加依赖:
- 如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用
spring-boot-starter-quartz
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
- 如果是1.5.9则要使用以下添加依赖:
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
2.这里我使用SpringBoot版本是2.0.0.BUILD-SNAPSHOT
,该版本开始集成了Quartz,所以事实现起来很方便。其它好像比较麻烦,这里就不介绍,以后有时间再详细深入了解Quartz。
创建任务类TestQuartz,该类主要是继承了QuartzJobBean
public class TestQuartz extends QuartzJobBean {
/**
* 执行定时任务
* @param jobExecutionContext
* @throws JobExecutionException
*/
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("quartz task "+new Date());
}
}
3.创建配置类QuartzConfig
@Configuration
public class QuartzConfig {
@Bean
public JobDetail teatQuartzDetail(){
return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();
}
@Bean
public Trigger testQuartzTrigger(){
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(10) //设置时间周期单位秒
.repeatForever();
return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())
.withIdentity("testQuartz")
.withSchedule(scheduleBuilder)
.build();
}
}
4.启动项目