【定时任务】——Spring定时任务Scheduled
定时任务在日常开发过程中非常常见,而且在日常的项目开发中也有多种实现方式,而且做任务调度的框架有很多种,小编最近的感受,如果想真正使用好任务调度还是存在困难的,所以分步学习,逐个击破!在这篇文章小编主要写spring中Scheduled。
spring定时任务设置有两种方式,注解和xml配置。推荐使用注解,在本文章也主要介绍注解方式配置。
注解方式配置定时任务
下面的步骤默认spring的其他配置项都已经配置好(比如启动注解配置,包路径扫描等)
1:在spring配置文件中配置,添加命名空间
- xmlns添加:
xmlns:task="http://www.springframework.org/schema/task"
- xsi:schemaLocation添加
- 注意"4.3"这是版本号,要修改和你的其他xsd版本号一致
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd"
- 启动注解驱动
- 注意“dataScheduler”为自定义名称,可以通过自己的业务定义 合适 的名称
<task:annotation-driven scheduler="dataScheduler"/>
- 开启任务调度器,并配置线程池大小
- 注意此处的id指定的就是上面的自定义名称
- spring的任务调度默认是单线程的,如果你的项目会有多任务定时执行,并且执行时间会相交的话,应该根据任务的具体执行情况配置线程池大小
- 如果不配置线程池,并且A和B任务在同一时间执行,A先执行的话,B要等待A执行完才可以执行,AB不会同时执行
<task:scheduler id="dataScheduler" pool-size="5"/>
2:使用注解配置定时任务
- 在你需要配置定时任务的方法上使用注解@Scheduled即可,下面一个简单案例:
- 注意 下面的案例是在每天的早上2点执行
- “0 0 2 * * *”是怎么组合的?下面会详细介绍@Scheduled()注解
@Scheduled(cron = "0 0 2 * * *")
public void init(){todo...
}
在此需要注意:@Scheduled只能注释在无参的方法上,我看网上有许多博客说必须无参无返回值的,但是经过我的测试有返回值是可以的,可能是版本更新了吧。
@Scheduled
@Scheduled注解是Spring专门为定时任务设计的注解
首先,让我们来看看这个注解是怎么组成的吧(适用于版本JDK8与spring4.3及其以上)
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {String cron() default "";String zone() default "";long fixedDelay() default -1L;String fixedDelayString() default "";long fixedRate() default -1L;String fixedRateString() default "";long initialDelay() default -1L;String initialDelayString() default "";
}
从上述代码中看以看出:
1:@Scheduled被注解部分:
- 元注解@Target表明@Scheduled注解可以在方法上使用(ElementType.METHOD),也可以作为元注解对其他注解进行注解(ElementType.ANNOTATION_TYPE)
- 元注解@Retention表明此注解会被JVM所保留,也就是会保存在运行时(RetentionPolicy.RUNTIME)
- 元注解@Documented表明此注解应该被 javadoc工具记录。默认情况下javadoc是不包括注解的。
- JDK8添加的注解@Repeatable表明此注解可以在同一个地方被重复使用
上述的所涉及到的注解有不清楚作用的,可以自行baidu\google,网上有好多介绍的文章。
2:@Scheduled参数部分,总共包含8各部分,我们来分别看一下其作用:
- cron:一个类似cron的表达式,扩展了通常的UN * X定义,包括秒,分,时,星期,月,年的触发器。
- fixedDelay:在最后一次调用结束和下一次调用开始之间以固定周期(以毫秒为单位)执行带注释的方法。(要等待上次任务完成后)
- fixedDelayString:同上面作用一样,只是String类型
- fixedRate:在调用之间以固定的周期(以毫秒为单位)执行带注释的方法。(不需要等待上次任务完成)
- fixedRateString:同上面作用一样,只是String类型
- initialDelay:第一次执行fixedRate()或fixedDelay()任务之前延迟的毫秒数 。
- initialDelayString:同上面作用一样,只是String类型
- zone:指明解析cron表达式的时区。
cron可以组合出更多的定时情况,fixedDelay和fixedRate只能定义每隔多长时间执行一次。
在上述cron、fixedDelay、fixedRate 只能同时存在一个,使用其中一个就不能使用另外的一个,否则会报错“java.lang.IllegalStateException”
原理简介
1:主要过程:
- spring在使用applicationContext将类全部初始化。
- 调用ScheduledAnnotationBeanPostProcessor类中的postProcessAfterInitialization方法获取项目中所有被注解 @Scheduled注解的方法 。
- 通过processScheduled方法将所有定时的方法存放在Set tasks = new LinkedHashSet(4); 定时任务队列中,并解析相应的参数。顺序存放,任务也是顺序执行。存放顺序为cron>fixedDelay>fixedRate
- 将解析参数后的定时任务存放在一个初始容量为16 的map中,key为bean name,value为定时任务:private final Map<Object, Set> scheduledTasks = new IdentityHashMap(16);
- 之后交给ScheduledTaskRegistrar类的方法scheduleTasks去添加定时任务。
2:上述就是一个大致过程,下面看一下相应的源码:
注意 :spring对定时任务的操作的源码全部在spring-context.jar包下的org.springframework.scheduling包下面,主要包含三部分:annotation、config、 support,大家有兴趣的话可以去看看
1:获取项目中所有被注解 @Scheduled注解的方法
public Object postProcessAfterInitialization(Object bean, String beanName) {Class<?> targetClass = AopProxyUtils.ultimateTargetClass(bean);if (!this.nonAnnotatedClasses.contains(targetClass)) {Map<Method, Set<Scheduled>> annotatedMethods = MethodIntrospector.selectMethods(targetClass, new MetadataLookup<Set<Scheduled>>() {public Set<Scheduled> inspect(Method method) {//获取注解方法**Set<Scheduled> scheduledMethods = AnnotatedElementUtils.getMergedRepeatableAnnotations(method, Scheduled.class, Schedules.class);**return !scheduledMethods.isEmpty() ? scheduledMethods : null;}});if (annotatedMethods.isEmpty()) {...} else {Iterator var5 = annotatedMethods.entrySet().iterator();while(var5.hasNext()) {Entry<Method, Set<Scheduled>> entry = (Entry)var5.next();Method method = (Method)entry.getKey();Iterator var8 = ((Set)entry.getValue()).iterator();while(var8.hasNext()) {Scheduled scheduled = (Scheduled)var8.next();//将获取的任务进行参数解析并存放到任务队列this.processScheduled(scheduled, method, bean);}}...}}return bean;
}
2:通过processScheduled方法将所有定时的方法存放在定时任务队列中
protected void processScheduled(Scheduled scheduled, Method method, Object bean) {try {...//解析initialDelayString参数String initialDelayString = scheduled.initialDelayString();if (StringUtils.hasText(initialDelayString)) {...}//解析cron参数String cron = scheduled.cron();if (StringUtils.hasText(cron)) {...//存放到任务队列中tasks.add(this.registrar.scheduleCronTask(new CronTask(runnable, new CronTrigger(cron, timeZone))));}...//解析fixedDelay参数long fixedDelay = scheduled.fixedDelay();if (fixedDelay >= 0L) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule = true;tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));}String fixedDelayString = scheduled.fixedDelayString();if (StringUtils.hasText(fixedDelayString)) {...//存放到任务队列中tasks.add(this.registrar.scheduleFixedDelayTask(new IntervalTask(runnable, fixedDelay, initialDelay)));}//解析fixedRate参数long fixedRate = scheduled.fixedRate();if (fixedRate >= 0L) {Assert.isTrue(!processedSchedule, errorMessage);processedSchedule = true;tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));}String fixedRateString = scheduled.fixedRateString();if (StringUtils.hasText(fixedRateString)) {...//存放到任务队列中tasks.add(this.registrar.scheduleFixedRateTask(new IntervalTask(runnable, fixedRate, initialDelay)));}Assert.isTrue(processedSchedule, errorMessage);Map var19 = this.scheduledTasks;//并发控制并将任务存放在map中synchronized(this.scheduledTasks) {Set<ScheduledTask> registeredTasks = (Set)this.scheduledTasks.get(bean);if (registeredTasks == null) {registeredTasks = new LinkedHashSet(4);//将任务存放在map中this.scheduledTasks.put(bean, registeredTasks);}((Set)registeredTasks).addAll(tasks);}} catch (IllegalArgumentException var26) {throw new IllegalStateException("Encountered invalid @Scheduled method '" + method.getName() + "': " + var26.getMessage());}
}
3:之后交给ScheduledTaskRegistrar类的方法scheduleTasks去添加定时任务
protected void scheduleTasks() {if (this.taskScheduler == null) {this.localExecutor = Executors.newSingleThreadScheduledExecutor();this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);}Iterator var1;if (this.triggerTasks != null) {var1 = this.triggerTasks.iterator();while(var1.hasNext()) {TriggerTask task = (TriggerTask)var1.next();this.addScheduledTask(this.scheduleTriggerTask(task));}}if (this.cronTasks != null) {var1 = this.cronTasks.iterator();while(var1.hasNext()) {CronTask task = (CronTask)var1.next();this.addScheduledTask(this.scheduleCronTask(task));}}IntervalTask task;if (this.fixedRateTasks != null) {var1 = this.fixedRateTasks.iterator();while(var1.hasNext()) {task = (IntervalTask)var1.next();this.addScheduledTask(this.scheduleFixedRateTask(task));}}if (this.fixedDelayTasks != null) {var1 = this.fixedDelayTasks.iterator();while(var1.hasNext()) {task = (IntervalTask)var1.next();this.addScheduledTask(this.scheduleFixedDelayTask(task));}}
}
此部分只是对原理进行了简单的介绍,如果有兴趣深入了解,可以去看看源码~
其他
做定时任务还可以使用java自带的原生API,Timer和TimerTask去设计。
- Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
- TimerTask:定义一个被执行的任务,Timer 安排该任务为一次执行或重复执行的任务。
可以这样理解Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,而TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。
这里就简单的提一下,并不是本文的重点,具体的用法自行google吧~
【定时任务】——Spring定时任务Scheduled相关推荐
- Spring 的@Scheduled注解实现定时任务运行和调度
Spring 的@Scheduled注解实现定时任务运行和调度 首先要配置我们的spring.xml --- 即spring的主配置文件(有的项目中叫做applicationContext.xm ...
- java 注解scheduler_使用spring的@Scheduled注解执行定时任务,启动项目不输出警告
在applicationContext.xml中添加: xmlns:task="http://www.springframework.org/schema/task" xsi:sc ...
- Spring的@Scheduled注解实现定时任务
Spring的@Scheduled注解实现定时任务 [简介篇] 项目经常会用到定时任务,实现定时任务的方式有很多种.在Spring框架中,实现定时任务很简单,常用的实现方式是使用注解@Schedule ...
- Spring定时任务@scheduled多线程的使用(@Async注解)
1.开篇 在Spring定时任务@Scheduled注解使用方式浅窥这篇文章里面提及过,spring的定时任务默认是单线程的,他在某些场景下会造成堵塞,那么如果我们想让每一个任务都起一条线程去执行呢? ...
- Spring定时任务-@Scheduled
目的:使用Spring的@Scheduled实现定时任务 1.在spring的配置文件中加入以下配置: xmlns:task="http://www.springframework.org/ ...
- Spring定时任务scheduled
Spring定时任务 一. cron 表达式 1. 概念:Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,Cron有如下两种语法格式: 2. Cro ...
- spring定时任务(Scheduled)运行阻塞不执行/Redission分布式锁阻塞问题
spring定时任务(Scheduled)运行阻塞不执行/Redission分布式锁阻塞问题 最近项目中发现一个bug,排查了很久,最后发现问题所在,在此记录一下. 问题描述: 项目运行一段时间后,c ...
- spring定时任务Scheduled与定时任务线程池配置SchedulingConfigurer ,Java
spring定时任务Scheduled与定时任务线程池配置SchedulingConfigurer ,Java spring默认定时任务的使用 package zhangphil.demo;impor ...
- Spring定时任务@Scheduled注解使用方式浅窥(cron表达式、fixedRate和fixedDelay)
1.开篇 spring的@Scheduled定时任务相信大家都是十分熟悉.最近在使用过程中发现了一些问题,写篇文章,和大家分享一下.结论在最后,不想看冗长过程的小伙伴可以直接拉到最后看结论. 2.简单 ...
最新文章
- 利用蒙特卡罗法,国外老哥成功制造出 100% 投篮命中的篮板!
- 从投票应用说起,功能才不是轻社交App的核心呢!
- 【Unity】2.11 了解游戏有哪些分类对你开阔思路有好处
- RHEL5中YUM命令解决RPM包依赖性
- Spring Boot程序无法加载主类解决方法
- 蓝昭餐饮管理系统服务器无法连接,服务器安全加固操作指南.docx
- 2018,广东,深圳马峦山游玩攻略(含登山线路)
- Linux-pthread如何设置线程的优先级
- SpringBoot实战(十四):Spring Boot Admin 集成安全模块
- 【chrome插件】web版微信接入图灵机器人API实现自动回复
- 今天给同学写5个数据结构算法的题...感觉很有价值的几个题..感兴趣的坐下。。...
- 数字媒体声音设计 第一章 概述
- MAC wps中选中的页面和缩放打印
- execute immediate
- 测试几个免费在线音乐识别器
- OA系统中如何实现合同管理?
- 【ValueError: could not convert string to float: ‘young‘】python利用pandas对string类型的数据序列化
- Pr:Lumetri范围
- 羊le个羊 小游戏 简单源码开源 思路分析
- b2c项目基础架构分析(一)b2c 大型站点方案简述 已补充名词解释