创建定时任务
import com.babyeye.dao.UserDAO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; /** * @author Holley * @Description 定时任务 * @create 2018-08-30 14:29 **/ @Service public class ScheduledTaskService { private Logger log = LoggerFactory.getLogger(ScheduledTaskService.class); private SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD hh:mm:ss"); @Autowired private UserDAO userDAO; /** * @Author holley * @Description 每天早上八点将到期账号设置为已过期状态 * @Date 2018/8/30 15:04 * @Param [] * @return void */ @Scheduled(cron = "0 0 8 * * ?") public void accountOverdue(){ userDAO.overdueAccount(); } /** * @Author holley * @Description 延迟5秒执行一次 * @Date 2018/8/30 15:35 * @Param [] * @return void */ @Scheduled(fixedDelay = 5000) public void testFixedDelay(){ log.info("执行fixedDelay测试方法:" + sdf.format(System.currentTimeMillis())); } /** * @Author holley * @Description 5秒执行一次 * @Date 2018/8/30 15:36 * @Param [] * @return void */ @Scheduled(fixedRate = 5000) public void testFixedRate(){ log.info("执行fixedRate测试方法:" + sdf.format(System.currentTimeMillis())); } /** * @Author holley * @Description 第一次延迟三秒执行,之后每5秒执行一次 * @Date 2018/8/30 15:39 * @Param [] * @return void */ @Scheduled(initialDelay = 3000,fixedRate = 5000) public void testInitialDelay(){ log.info("执行initialDelay测试方法:" + sdf.format(System.currentTimeMillis())); } }
开启定时任务
import com.babyeye.interceptor.Interceptor; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @ServletComponentScan @SpringBootApplication @EnableScheduling public class OkjingApplication { public static void main(String[] args) { SpringApplication.run(OkjingApplication.class, args); } }
下面开始介绍下secheduled的参数都有哪些:
1.fixedDelay: 表示上一次任务执行多久后再次执行,参数类型是Long,单位毫秒
2.fixedDelayString : 与fixedDelay含义一样,只是参数类型变成了String
3.fixedRate : 表示按照一定的频率执行任务,参数类型是Long,单位毫秒
4.fixedRateString : 和fixedRate含义一样,只是参数类型变成了String
5.initialDelay : 表示延迟多久开始执行第一次任务,参数类型Long,单位毫秒
6.initialDelayString : 与initialDelay含义一样,只是参数类型变成了String
7.cron : cron表达式,指定任务在特定时间执行
cron表达式定义:
Cron表达式是一个字符串,由空格隔开的6个或7个域组成,每个域对应一个含义(秒,分,时,日,月,周 ,年),其中年是可选字段,但是spring的scheduled只支持前6个域的表达式,也就是不能设置年,如果超过则会报错
每个域中可出现的字符类型及含义:
(1)各域支持的字符类型
秒:可出现", - * /"四个字符,有效范围为0-59的整数
分:可出现", - * /"四个字符,有效范围为0-59的整数
时:可出现", - * /"四个字符,有效范围为0-23的整数
每月第几天:可出现", - * / ? L W C"八个字符,有效范围为0-31的整数
月:可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc
星期:可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推
(2)特殊字符含义
* : 表示匹配该域的任意值,比如在秒*, 就表示每秒都会触发事件。;
? : 只能用在每月第几天和星期两个域。表示不指定值,当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”;
- : 表示范围,例如在分域使用5-20,表示从5分到20分钟每分钟触发一次
/ : 表示起始时间开始触发,然后每隔固定时间触发一次,例如在分域使用5/20,则意味着每小时的第5分,25分,45分,分别触发一次.
, : 表示列出枚举值。例如:在分域使用5,20,则意味着在5和20分时触发一次。
L : 表示最后,只能出现在星期和每月第几天域,如果在星期域使用1L,意味着在最后的一个星期日触发。
W : 表示有效工作日(周一到周五),只能出现在每月第几日域,系统将在离指定日期的最近的有效工作日触发事件。注意一点,W的最近寻找不会跨过月份
LW : 这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
# : 用于确定每个月第几个星期几,只能出现在每月第几天域。例如在1#3,表示某月的第三个星期日。
C:代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。
实例:
"0 0 * * * *" 表示每小时0分0秒执行一次
" */10 * * * * *" 表示每10秒执行一次
"0 0 8-10 * * *" 表示每天8,9,10点执行
"0 0/30 8-10 * * *" 表示每天8点整开始到10点,每半小时执行
"0 0 9-17 * * 2-6" 表示每周一至周五,9点到17点的0分0秒执行
"0 0 0 25 12 ?" 表示每年圣诞节(12月25日)0时0分0秒执行
8.zone : 时区,默认为当前时区,一般没有用到
多线程执行
不知道有没有人注意到上面代码执行结果打印出来的log,在截图中我们可以发现被执行的每个任务都是被同一个线程所调用,这是因为在pspring中如果没有自定义设置线程池时,会默认创建一个单线程的线程池。这样的话,如果定时任务增多,就会容易造成阻塞。
如果改成多线程执行,在springboot中有两种方法。
一。实现SchedulingConfigurer接口,重写configureTasks方法即可,代码如下:
import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.config.ScheduledTaskRegistrar; import java.util.concurrent.Executors; @Configuration //所有的定时任务都放在一个线程池中,定时任务启动时使用不同都线程。 public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { //设定一个长度10的定时任务线程池 taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10)); }
}
二。使用config配置类的方式添加配置,然后需要在定时任务的类或方法上添加@Async注解,代码如下:
@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; } }
/** * @author Holley * @Description 定时任务 * @create 2018-08-30 14:29 **/ @Async @Service public class ScheduledTaskService { private Logger log = LoggerFactory.getLogger(ScheduledTaskService.class); @Autowired private UserDAO userDAO; /** * @Author holley * @Description 每天早上八点将到期账号设置为已过期状态 * @Date 2018/8/30 15:04 * @Param [] * @return void */ @Scheduled(cron = "0 0 8 * * ?") public void accountOverdue(){ userDAO.overdueAccount(); } }
除了上述的spring task 的scheduled之外,还有如下几种方式可以实现定时任务:
1.Timer:java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务,使用这种方式可以按照某一频率来执行,也可以设置指定时间,demo如下:
import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * @author Holley * @Description 请输入一句话进行描述 * @create 2018-08-31 10:45 **/ public class TimerDemo { public static void main(String[] args) { TimerTask timerTask = new TimerTask() { @Override public void run() { System.out.println("task run:"+ new Date()); } }; Timer timer = new Timer(); System.out.println(new Date()); //安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次 timer.schedule(timerTask,10000,3000); } }
执行结果如下:
指定时间执行:

public class TimerTest { public static void main(String[] args) { Calendar calendar = Calendar.getInstance(); /** * 指定触发的时间 */ calendar.set(Calendar.DAY_OF_MONTH,13);//设置日期为13号 calendar.set(Calendar.MONTH, 3);//设置日期为4月份(月份是从0开始计算) calendar.set(Calendar.HOUR_OF_DAY, 12); //设置15点的时候触发 calendar.set(Calendar.MINUTE, 0); //设置56分钟的时候触发 calendar.set(Calendar.SECOND, 1); //设置第一秒的时候触发 Date time = calendar.getTime(); Timer timer = new Timer(); timer.schedule(new RemindTask(), time); } }
public class RemindTask extends TimerTask { @Override public void run() { System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date())); } }
详细介绍可以参考:https://blog.csdn.net/u013164931/article/details/80762356
2.ScheduledExcutorService:也是jdk自带的一个类,是基于线程设计的定时任务,每个调度任务都会分配到线程池中的一个线程去执行,因此任务是并发执行的。demo如下:
import java.util.Date; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * @author Holley * @Description 请输入一句话进行描述 * @create 2018-08-31 10:58 **/ public class ScheduleExecutorService { public static void main(String[] args) { ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); // java8的Lambda表达式 参数:1、任务体 2、首次执行的延时时间 // 3、任务执行间隔 4、间隔时间单位 service.scheduleAtFixedRate(() -> System.out.println("task ScheduledExecutorService " + new Date()), 0, 3, TimeUnit.SECONDS); } }
在此demo中使用了java8的Lambda表达式,如果对此表达式不了解的可以参考下:https://www.cnblogs.com/andywithu/p/7357069.html
3.Quartz:这是一个功能比较强大的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来比较复杂,还需要添加依赖
如果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>
如果SpringBoot版本是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>
- 创建任务类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()); } }
- 创建配置类
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(); } }
关于Quartz的详细内容可以查看官方文档:http://www.quartz-scheduler.org/documentation/
参考:https://blog.csdn.net/ninifengs/article/details/77141240
https://blog.csdn.net/wqh8522/article/details/79224290