timer
quartz
spring的scheduled
首先我们来看看quartz的使用。
使用首先要明白quartz的三个核心概念:
示例: 每隔5秒中提醒一次该开会了
使用步骤:
<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.0</version> </dependency>
package demo; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.text.SimpleDateFormat; import java.util.Date; public class RemindJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { String remindTime = new SimpleDateFormat("yy-MM-dd HH:mm:ss:SSS").format(new Date()); System.out.println("提醒时间:" + remindTime + ", 该开会了..."); } }
package demo; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.util.concurrent.TimeUnit; public class SchedulerDemo { public static void main(String[] args) throws SchedulerException, InterruptedException { // 1、创建调度器Scheduler SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); // 2、创建 JobDetail 实例,并与 RemindJob 类绑定(Job执行内容) JobDetail jobDetail = JobBuilder.newJob(RemindJob.class) .withIdentity("remindJob", "group1").build(); // 3、构建 Trigger 实例,每隔5s执行一次 Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("trigger1", "triggerGroup1") .startNow()//立即生效 .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) //每隔5s执行一次 .repeatForever()) //一直执行 .build(); //4、执行 scheduler.scheduleJob(jobDetail, trigger); System.out.println("--------scheduler start ! ------------"); scheduler.start(); //睡眠 TimeUnit.MINUTES.sleep(1); scheduler.shutdown(); System.out.println("--------scheduler shutdown ! ------------"); } }
CronTrigger功能非常强大,是基于日历的作业调度,而SimpleTrigger是精准指定间隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表达式的,先了解下Cron表达式:
由7个子表达式组成字符串的,格式如下:
[秒] [分] [小时] [日] [月] [周] [年]
序号 | 说明 | 是否必填 | 允许填写的值 | 允许的通配符 |
---|---|---|---|---|
1 | 秒 | 是 | 0-59 | , - * / |
2 | 分 | 是 | 0-59 | , - * / |
3 | 时 | 是 | 0-23 | , - * / |
4 | 日 | 是 | 1-31 | , - * ? / L W |
5 | 月 | 是 | 1-12或JAN-DEC | , - * / |
6 | 周 | 是 | 1-7或SUN-SAT | , - * ? / L W |
7 | 年 | 否 | empty 或1970-2099 | , - * / |
通配符表示的意思: * 表示每x(秒,分,天,月,日,周几)的意思 0/5 * * * * ? : 从0秒开始,每增加5秒执行一次 0 0/5 * * * ? : 从0秒开始,每增加5分钟执行一次 5,8,12 * * * * ?: ,表示一个列表,表示在每一分钟里面的第5秒,第8秒,第12秒各执行一次 0 10 8,10,13 * * ? : 在8点,10点,13点的10分的时候,各执行一次 0 10 8-12 * * ?: -表示多少到多少之间,在8点到12的的每小时的第10分钟的时候执行任务 ?: 只能出现在表示日期或值星期几的位置 W: workday(工作日) L: 最后一天,或最后一周的周几 #: 表示第几周的意思
// 3、构建 Trigger 实例,每隔5s执行一次 Trigger trigger = TriggerBuilder .newTrigger() .withIdentity("trigger1", "triggerGroup1") .startNow()//立即生效 .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?")) .build();
public class QuartzJob1 extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { String remindTime = new SimpleDateFormat("yy-MM-dd HH:mm:ss:SSS").format(new Date()); System.out.println("提醒时间:" + remindTime + ", 该开会了..."); } }
@Configuration public class QuartzConfig { @Bean public JobDetail jobDetail1(){ return JobBuilder.newJob(QuartzJob1.class).storeDurably().build(); } @Bean public Trigger trigger1(){ SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(1) //每一秒执行一次 .repeatForever(); //永久重复,一直执行下去 return TriggerBuilder.newTrigger() .forJob(jobDetail1()) .withSchedule(scheduleBuilder) .build(); } /* @Bean(name = "jobDetail") public MethodInvokingJobDetailFactoryBean detailFactoryBean(TimingTask task) { // ScheduleTask为需要执行的任务 MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean(); /* * 是否并发执行 * 例如每3s执行一次任务,但是当前任务还没有执行完,就已经过了3s了. * 如果此处为true,则下一个任务会bing执行,如果此处为false,则下一个任务会等待上一个任务执行完后,再开始执行 */ jobDetail.setConcurrent(true); jobDetail.setName("scheduler");// 设置任务的名字 jobDetail.setGroup("scheduler_group");// 设置任务的分组,这些属性都可以存储在数据库中,在多任务的时候使用 /* * 这两行代码表示执行task对象中的scheduleTest方法。定时执行的逻辑都在scheduleTest。 */ jobDetail.setTargetObject(task); jobDetail.setTargetMethod("start"); return jobDetail; } @Bean public JobDetailFactoryBean jobDetail(){ JobDetailFactoryBean jobDetailFactoryBean = new JobDetailFactoryBean(); jobDetailFactoryBean.setJobClass(HiJob.class); jobDetailFactoryBean.setBeanName("hiJob"); jobDetailFactoryBean.setGroup("jobGroup"); jobDetailFactoryBean.setDurability(true); return jobDetailFactoryBean; } @Bean public CronTriggerFactoryBean cronTrigger(JobDetailFactoryBean jobDetailFactoryBean){ CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean(); triggerFactoryBean.setCronExpression("0/5 * * * * ?"); triggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject()); return triggerFactoryBean; } */ }
在SpringBoot使用Spring Schedule非常简单,因为SpringBoot自身的starter中已经集成了Schedule,而不需要我们做更多的处理。
使用@EnableScheduling注解开启定时功能,该注解可以使用在启动类上,也可以注解于定时任务的类上。然后使用@Scheduled注解配合其参数完成定时任务。
依赖
org.springframework:spring-context 包
@SpringBootApplication @EnableScheduling public class SpringBootQuartzDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzDemoApplication.class, args); } }
@Component public class ScheduledComponet { private static Logger LOGGER = LoggerFactory.getLogger(ScheduledComponet.class) ; @Scheduled(fixedDelay = 5000) public void testSheduled() throws InterruptedException { TimeUnit.SECONDS.sleep(6); LOGGER.info("定时任务1: 线程: [ {} ],[ {} ]",Thread.currentThread().getName(),new Date()); } @Scheduled(fixedRate= 3000) public void testSheduled2() throws InterruptedException { TimeUnit.SECONDS.sleep(4); LOGGER.info("定时任务2: 线程: [ {} ],[ {} ]",Thread.currentThread().getName(),new Date()); } @Scheduled(cron = "0/5 * * ? * *") public void testSheduled3() throws InterruptedException { TimeUnit.SECONDS.sleep(8); LOGGER.info("定时任务3: 线程: [ {} ],[ {} ]",Thread.currentThread().getName(),new Date()); } }
注意
多个 @Scheduled 注解的方法是在一个线程中执行的,如果运行比较耗时,会阻塞其他任务的执行,使用 @Async 和 @EnableAsync 开启异步任务,可以解决,会使用异步任务线程池来运行. spring 的 cron 表达式中不允许出现字母.
@SpringBootApplication @EnableScheduling @EnableAsync // 开启异步 public class SpringBootQuartzDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringBootQuartzDemoApplication.class, args); } }
@Scheduled(fixedDelay = 5000) @Async // 方法异步执行 public void testSheduled() throws InterruptedException { TimeUnit.SECONDS.sleep(6); LOGGER.info("定时任务1: 线程: [ {} ],[ {} ]",Thread.currentThread().getName(),new Date()); }