简介
文章中代码案例曾经同步到码云:代码中的schedule-demo
中。
定时工作是指调度程序在指定的工夫或周期触发执行的工作
应用场景:发送邮件、统计、状态批改、音讯推送、流动开启、增量索引
现有的定时工作技术
- Java自带的java.util.Timer类,这个类容许你调度一个java.util.TimerTask工作。应用这种形式能够让你的程序依照某一个频度执行,但不能在指定工夫运行。应用较少。(不举荐应用,代码案例中曾经给出阐明)
- Spring3.0当前自主开发的定时工作工具spring task,应用简略,反对线程池,能够高效解决许多不同的定时工作,除spring相干的包外不须要额定的包,反对注解和配置文件两种模式。 不能解决过于简单的工作
- 业余的定时框架quartz,功能强大,能够让你的程序在指定工夫执行,也能够依照某一个频度执行,反对数据库、监听器、插件、集群
代码实例
1.Timer
import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * @Author: njitzyd * @Date: 2021/1/14 22:27 * @Description: Java自带的Timer类 * @Version 1.0.0 */public class MyTimer { public static void main(String[] args) {// 多线程并行处理定时工作时,Timer运行多个TimeTask时,只有其中之一没有捕捉抛出的异样,其它工作便会主动终止运行,应用ScheduledExecutorService则没有这个问题。//// //org.apache.commons.lang3.concurrent.BasicThreadFactory// ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,// new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());// executorService.scheduleAtFixedRate(new Runnable() {// @Override// public void run() {// //do something// }// },initialDelay,period, TimeUnit.HOURS); try { // 创立定时器 Timer timer = new Timer(); // 增加调度工作 // 安顿指定的工作在指定的工夫开始进行反复的 固定提早执行 timer.schedule(new MyTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-01-14 22:43:10"),10*1000); // 安顿指定的工作在指定的提早后开始进行反复的 固定速率执行 //timer.scheduleAtFixedRate(new MyTask(),new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-01-14 22:43:10"),10*1000); } catch (ParseException e) { e.printStackTrace(); } }}/** * 自定义的工作类 */class MyTask extends TimerTask { // 定义调度工作 public void run() { System.out.println("log2:"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); }}
2.Spring Task
配置有两种形式,一种是基于注解,一种是基于配置文件。在springboot中举荐应用注解和配置类的形式,这里咱们次要应用注解和配置类,基于配置文件的也会给出demo。
- 基于注解
在springboot的启动类上通过注解@EnableScheduling
开启。而后在类的办法上通过@Scheduled
注解应用,代码案例如下:
@Componentpublic class ScheduleTest { @Scheduled(fixedDelayString = "5000") public void testFixedDelayString() { System.out.println("Execute at " + System.currentTimeMillis()); }}
具体的应用能够参考我的另一篇博客:@shcedule注解的应用
- 基于xml配置
首先是工作类:
/** * 工作类 * @author 朱友德 */public class SpringTask { private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void m1(){ System.out.println("m1:"+simpleDateFormat.format(new Date())); } public void m2(){ System.out.println("m2:"+simpleDateFormat.format(new Date())); } public void m3(){ System.out.println("m2:"+simpleDateFormat.format(new Date())); }}
而后是xml配置:
<!--spring-task.xml配置--><bean id="springTask" class="com.njit.springtask.SpringTask"></bean> <!--注册调度工作--> <task:scheduled-tasks> <!--提早8秒 执行工作--> <!--<task:scheduled ref="springTask" method="m1" fixed-delay="8000" />--> <!--固定速度5秒 执行工作--> <!--<task:scheduled ref="springTask" method="m2" fixed-rate="5000"/>--> <!-- 应用cron表达式 指定触发工夫 spring task 只反对6位的cron表达式 秒 分 时 日 月 星期 --> <task:scheduled ref="springTask" method="m3" cron="50-59 * * ? * *"/> </task:scheduled-tasks> <!--执行器配置--> <task:executor id="threadPoolTaskExecutor" pool-size="10" keep-alive="5"></task:executor> <!--调度器配置--> <task:scheduler id="threadPoolTaskScheduler" pool-size="10"></task:scheduler>
3.quartz
首先咱们要理解一下quartz
中的一些基本概念:
- Scheduler:任务调度器,是理论执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
Trigger:触发器,用于定义任务调度的工夫规定,有SimpleTrigger,CronTrigger,DateIntervalTrigger等,其中CronTrigger用的比拟多,本文次要介绍这种形式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
- SimpleTrigger:简略触发器,从某个工夫开始,每隔多少工夫触发,反复多少次。
- CronTrigger:应用cron表达式定义触发的工夫规定,如"0 0 0,2,4 1/1 ? " 示意每天的0,2,4点触发。
- DailyTimeIntervalTrigger:每天中的一个时间段,每N个工夫单元触发,工夫单元能够是毫秒,秒,分,小时
- CalendarIntervalTrigger:每N个工夫单元触发,工夫单元能够是毫秒,秒,分,小时,日,月,年。
- Calendar:它是一些日历特定工夫点的汇合。一个trigger能够蕴含多个Calendar,以便排除或蕴含某些工夫点。
- JobDetail:用来形容Job实现类及其它相干的动态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只须要执行某个类的某个办法,就能够通过MethodInvokingJobDetailFactoryBean来调用。
- Job:是一个接口,只有一个办法void execute(JobExecutionContext context),开发者实现该接口定义运行工作,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保留在JobDataMap实例中。实现Job接口的工作,默认是无状态的,若要将Job设置成有状态的(即是否反对并发),在quartz中是给实现的Job增加@DisallowConcurrentExecution注解
Quartz 任务调度的外围元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是理论执行调度的控制器。在 Quartz 中,trigger 是用于定义调度工夫的元素,即依照什么工夫规定去执行工作。Quartz 中次要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DailyTimeIntervalTrigger,和 CalendarIntervalTrigger
在 Quartz 中,job 用于示意被调度的工作。次要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的工作被执行完之后,能力触发下一次执行。Job 次要有两种属性:volatility 和 durability,其中 volatility 示意工作是否被长久化到数据库存储,而 durability 示意在没有 trigger 关联的时候工作是否被保留。两者都是在值为 true 的时候工作被长久化或保留。一个 job 能够被多个 trigger 关联,然而一个 trigger 只能关联一个 job
- 引入starter依赖
<!-- quartz --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
- 编写两个工作Task
/** * @author * 工作一 */public class TestTask1 extends QuartzJobBean{ @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("TestQuartz01----" + sdf.format(new Date())); }}/** * 工作二 * @author */public class TestTask2 extends QuartzJobBean{ @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("TestQuartz02----" + sdf.format(new Date())); }}
- 编写配置类
/** * quartz的配置类 */@Configurationpublic class QuartzConfig { @Bean public JobDetail testQuartz1() { return JobBuilder.newJob(TestTask1.class).withIdentity("testTask1").storeDurably().build(); } @Bean public Trigger testQuartzTrigger1() { //5秒执行一次 SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) .repeatForever(); return TriggerBuilder.newTrigger().forJob(testQuartz1()) .withIdentity("testTask1") .withSchedule(scheduleBuilder) .build(); } @Bean public JobDetail testQuartz2() { return JobBuilder.newJob(TestTask2.class).withIdentity("testTask2").storeDurably().build(); } @Bean public Trigger testQuartzTrigger2() { //cron形式,每隔5秒执行一次 return TriggerBuilder.newTrigger().forJob(testQuartz2()) .withIdentity("testTask2") .withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?")) .build(); }}
- 启动我的项目察看
能够失常的看到工作失常启动,工作Task被执行:
实现原理
1.Timer
简略来说就是执行时把Task放到队列中,而后有个线程(留神他是单线程的,如果执行多个Task,一个抛出异样就会导致整个都蹦)会去拉取最近的工作(队列中是依据下次执行工夫进行排序)去执行,如果工夫没到则wait()办法期待。
而ScheduledThreadPoolExecutor
的执行步骤是,执行时向队列中增加一条工作,队列外部依据执行工夫程序进行了排序。而后线程池中的线程来获取要执行的工作,如果工作还没到执行工夫就在这等,等到工作能够执行,而后获取到ScheduledFutureTask
执行,执行后批改下次的执行工夫,再增加到队列中去。
ScheduledThreadPoolExecutor的运行机制
Timer的应用以及执行原理
2.spring task
在springboot中,应用`@schedule注解默认是单线程的,多个工作执行起来工夫会有问题:B工作会因为A工作执行起来须要20S而被延后20S执行。所以咱们有两个计划去解决这个问题
- 在办法上应用
@Async
注解 - 指定线程池
这里次要介绍第二种,只须要配置一个配置类即可:
@Configurationpublic class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10)); }}
上面介绍原理:
jdk的线程池和工作调用器别离由ExecutorService、ScheduledExecutorService定义,继承关系如下:
ThreadPoolExecutor:ExecutorService的实现类,其构造函数提供了灵便的参数配置,可结构多种类型的线程池
ScheduledThreadPoolExecutor:ScheduledExecutorService的实现类,用于任务调度
spring task对定时工作的两个形象:
- TaskExecutor:与jdk中Executor雷同,引入的目标是为定时工作的执行提供线程池的反对,如果设置,默认只有一个线程。
- TaskScheduler:提供定时工作反对,须要传入一个Runnable的工作做为参数,并指定须要周期执行的工夫或者触发器,这样
Runnable
工作就能够周期性执行了。
继承关系如下:
工作执行器与调度器的实现类别离为ThreadPoolTaskExecutor、ThreadPoolTaskScheduler
TaskScheduler须要传入一个Runnable的工作做为参数,并指定须要周期执行的工夫或者触发器(Trigger
)。
spring定义了Trigger接口的实现类CronTrigger,反对应用cron表达式指定定时策略,应用如下:
scheduler.schedule(task, new CronTrigger("30 * * * * ?"));
在springboot我的项目中,咱们个别都是应用@schedule
注解来应用spring task,这个注解外部的实现就是应用下面的内容。
spring在初始化bean后,通过postProcessAfterInitialization
拦挡到所有的用到@Scheduled
注解的办法,并解析相应的的注解参数,放入“定时工作列表”期待后续解决;之后再“定时工作列表”中对立执行相应的定时工作(工作为程序执行,先执行cron,之后再执行fixedRate)
源码解析
3.quartz
原理参考这篇文章:
quartz原理
参考
为什么应用ScheduledThreaedPoolExcutor而不是Timer
quartz原理解析
quartz原理