为企业做网站电话开场白,wordpress首页置顶推荐问题,万网域名抢注,买源码做网站文章目录 1. cron表达式生成器2. 简单定时任务代码示例#xff1a;每隔两秒打印一次字符3. Scheduled注解的参数3.1 cron3.2 fixedDelay3.3 fixedRate3.4 initialDelay3.5 fixedDelayString、fixedRateString、initialDelayString等是String类型#xff0c;支持占位符3.6 tim… 文章目录 1. cron表达式生成器2. 简单定时任务代码示例每隔两秒打印一次字符3. Scheduled注解的参数3.1 cron3.2 fixedDelay3.3 fixedRate3.4 initialDelay3.5 fixedDelayString、fixedRateString、initialDelayString等是String类型支持占位符3.6 timeUnit 4. 问题定时器的任务默认是按照顺序执行的可能导致一些任务无法执行4.1 ScheduledAnnotationBeanPostProcessor类处理器解析带有Scheduled注解的方法4.2 processScheduled方法处理Scheduled注解后面的参数并将其添加到任务列表中4.3 执行任务。ScheduledTaskRegistrar类为Spring容器的定时任务注册中心。Spring容器通过线程处理注册的定时任务 5. 问题当系统时间发生改变时Scheduled注解失效 1. cron表达式生成器
cron表达式生成器https://cron.qqe2.com/
2. 简单定时任务代码示例每隔两秒打印一次字符
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;Service
EnableScheduling
public class ScheduleDemo1 {Scheduled(cron */2 * * * * ?)public static void test() {// 十六进制转换为字符System.out.print((char) Integer.parseInt(5fc3, 16));System.out.print((char) Integer.parseInt(6d41, 16));System.out.print((char) Integer.parseInt(65f6, 16));System.out.print((char) Integer.parseInt(95f4, 16));System.out.println();}
}输出 3. Scheduled注解的参数 3.1 cron
参数接收一个cron表达式cron表达式是一个以空格为间隔符来区分不同域的字符串总共有6个或7个域。cron表达式从左到右每个域分别标识的[秒] [分] [小时] [日] [月] [周] [年]其中[年]不是必选的域可以省略。
序号域必填值的范围允许的通配符1秒是0-59, - * /2分是0-59, - * /3时是0-23, - * /4日是1-31, - * ? / L W5月是1-12 / JAN-DEC, - * /6周是1-7 or SUN-SAT, - * ? / L #7年否1970-2099, - * / 通配符说明 * 表示所有值例如在时的字段上设置 *表示每一个小时都会触发。 ? 表示不指定值即当前使用的场景为不需要关心这个字段设置的值。例如要在每月的10号触发一个操作但不关心是周几所以需要周位置的那个字段设置为“?” 具体设置为 0 0 0 10 * ? 。 - 表示区间例如在小时上设置 “10-12”,表示 10,11,12点都会触发。 , 表示指定多个值例如在周字段上设置 “MON,WED,FRI” 表示周一周三和周五触发。 / 用于递增触发如在秒上面设置“5/15” 表示从5秒开始每隔15秒触发(5,20,35,50)。在日字段上设置‘1/3’所示每月1号开始每隔三天触发一次 L 表示最后的意思在日字段设置上表示当月的最后一天(依据当前月份如果是二月还会依据是否是闰年) 在周字段上表示星期六相当于“7”或“SAT”。如果在“L”前加上数字则表示该数据的最后一个。例如在周字段上设置“6L”这样的格式则表示“本月最后一个星期五”。 W表示离指定日期的最近那个工作日(周一至周五)。例如在日字段上置“15W”表示离每月15号最近的那个工作日触发。如果15号正好是周六则找最近的周五(14号)触发, 如果15号是周未则找最近的下周一(16号)触发。如果15号正好在工作日(周一至周五)则就在该天触发。如果指定格式为 “1W”它则表示每月1号往后最近的工作日触发。如果1号正是周六则将在3号下周一触发。(注“W”前只能设置具体的数字不允许区间“-”)。 #序号(表示每月的第几个周几)例如在周字段上设置“6#3”表示在每月的第三个周六。注意如果指定“#5”正好第五周没有周六则不会触发该配置小提示‘L’和 ‘W’可以一组合使用。如果在日字段上设置“LW”则表示在本月的最后一个工作日触发周字段的设置若使用英文字母是不区分大小写的即MON与mon相同。 示例 每隔5秒执行一次*/5 * * * * ? 每隔1分钟执行一次0 */1 * * * ? 每天23点执行一次0 0 23 * * ? 每天凌晨1点执行一次0 0 1 * * ? 每月1号凌晨1点执行一次0 0 1 1 * ?
3.2 fixedDelay
上一次执行完成后延迟多久执行下一次以上一次任务执行的完成时间开始延迟如
Scheduled(fixedDelay 5000) //上一次执行完成后延迟5s再执行3.3 fixedRate
固定延迟多久执行下一次任务不依赖于上一次任务执行成功的时间如
Scheduled(fixedRate 5000) //上一次执行后延迟5s就开始执行3.4 initialDelay
启动后延迟多久后执行第一次可根据场景搭配fixedRate或fixedDelay实现定时调度如
Scheduled(initialDelay 5000,fixedRate 300000) //启动后延迟5s执行之后每次执行时间间隔5min3.5 fixedDelayString、fixedRateString、initialDelayString等是String类型支持占位符 如Scheduled(fixedDelayString “${task.fixed-delay}”) 3.6 timeUnit
时间单位默认毫秒
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;4. 问题定时器的任务默认是按照顺序执行的可能导致一些任务无法执行
我创建定时器执行任务目的是为了让它多线程执行任务但是后来才发现Scheduled注解的方法默认是按照顺序执行的这会导致当一个任务挂死的情况下其它任务都在等待无法执行。
Scheduled注解加载的过程以及它是如何执行的
4.1 ScheduledAnnotationBeanPostProcessor类处理器解析带有Scheduled注解的方法 4.2 processScheduled方法处理Scheduled注解后面的参数并将其添加到任务列表中 4.3 执行任务。ScheduledTaskRegistrar类为Spring容器的定时任务注册中心。Spring容器通过线程处理注册的定时任务
首先调用scheduleCronTask初始化定时任务。 然后在ThreadPoolTaskScheduler类中会对线程池进行初始化线程池的核心线程数量为1
private volatile int poolSize 1;阻塞队列为DelayedWorkQueue。 因此原因就找到了当有多个方法使用Scheduled注解时就会创建多个定时任务到任务列表中当其中一个任务没执行完时其它任务在阻塞队列当中等待因此所有的任务都是按照顺序执行的只不过由于任务执行的速度相当快让我们感觉任务都是多线程执行的。
下面举例来验证一下将上述的某个定时任务添加睡眠时间观察另一个定时任务是否输出。
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;Slf4j
EnableScheduling
Component
public class ScheduleDemo2 {private static final ThreadLocalInteger threadLocalA new ThreadLocal();Scheduled(cron 0/2 * * * * ?)public void taskA() {try {log.info(执行了ScheduleTask类中的taskA方法);Thread.sleep(TimeUnit.SECONDS.toMillis(10));} catch (InterruptedException e) {e.printStackTrace();}}Scheduled(cron 0/1 * * * * ?)public void taskB() {int num threadLocalA.get() null ? 0 : threadLocalA.get();log.info(taskB方法执行次数{}, num);threadLocalA.set(num);}
} 输出可以观察到两个定时任务不是同时执行的是按顺序执行的 想要避免顺序执行进行并发就要配置定时任务线程池
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;Configuration
public class ScheduleConfig implements SchedulingConfigurer {Overridepublic void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.setScheduler(getExecutor());}Beanpublic Executor getExecutor(){return new ScheduledThreadPoolExecutor(5);}
}输出可以观察到两个定时任务不是顺序执行了从出现次数的乱序这种多线程问题也可以看出是并发执行了 从输出结果我们可以看到即使testA休眠但是testB仍然正常执行并且其还复用了其它线程导致执行次数发生了变化。
5. 问题当系统时间发生改变时Scheduled注解失效
另外一种情况就是在配置完线程池之后当你手动修改服务器时间时目前我做的测试就是服务器时间调前则会导致注解失效而服务器时间调后则不会影响注解的作用。 原因 JVM启动之后会记录当前系统时间然后JVM根据CPU ticks自己来算时间此时获取的是定时任务的基准时间。如果此时将系统时间进行了修改当Spring将之前获取的基准时间与当下获取的系统时间进行比对不一致就会造成Spring内部定时任务失效。因为此时系统时间发生变化了不会触发定时任务。 解决办法 重启项目 不使用Scheduled注解改成ScheduledThreadPoolExecutor进行替代部分代码
实际项目中一般使用xxl-job、Quartz等框架Scheduled注解会使用的话也是定时更新一些变量的值大量的定时任务还是使用专门的定时任务框架实现
参考1 参考2 参考3