原标题:教你优雅的实现 SpringBoot 并行任务
不点,我们哪来故事?
第一种:把参数配置到.properties文件中:
第二种定时任务:单线程和多线程
1、创建定时任务:
2、开启定时任务:
3、执行结果(单线程)
4、多线程处理定时任务:
5、执行结果(并发)
Spring Boot 的定时任务:
第一种:把参数配置到.properties文件中:
代码:
packagecom.accord.task;
importjava.text.SimpleDateFormat;
importjava.util.Date;
importorg.springframework.scheduling.annotation.Scheduled;
importorg.springframework.stereotype.Component;
/**
* 从配置文件加载任务信息
*@author王久印
*/
@Component
publicclassScheduledTask{
privatestaticfinalSimpleDateFormat dateFormat =newSimpleDateFormat(“HH:mm:ss”);
//@Scheduled(fixedDelayString = “${jobs.fixedDelay}”)
@Scheduled(fixedDelayString =“2000”)
publicvoidgetTask1{
System.out.println(“任务1,从配置文件加载任务信息,当前时间:”+ dateFormat.format(newDate));
}
@Scheduled(cron =“${jobs.cron}”)
publicvoidgetTask2{
System.out.println(“任务2,从配置文件加载任务信息,当前时间:”+ dateFormat.format(newDate));
}
}
application.properties文件:
jobs.fixedDelay=5000
jobs.cron=0/5* * * * ?
SpringBootCron2Application.java中:
packagecom.accord;
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
publicclassSpringBootCron2Application{
publicstaticvoidmain(String[] args){
SpringApplication.run(SpringBootCron2Application.class,args);
}
}
注:@EnableScheduling 这个一定要加上;否则,不会定时启动任务!
@Scheduled中的参数说明:
@Scheduled(fixedRate=2000) :上一次开始执行时间点后2秒再次执行;
@Scheduled(fixedDelay=2000) :上一次执行完毕时间点后2秒再次执行;
@Scheduled(initialDelay=1000, fixedDelay=2000) :第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行;
@Scheduled(cron=”* * * * * ?”) :按cron规则执行。
在线Cron表达式生成器: http://cron.qqe2.com/
第二种定时任务:单线程和多线程1、创建定时任务:packagecom.accord.task;基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.scheduling.annotation.Scheduled;
importorg.springframework.stereotype.Component;
/**
* 构建执行定时任务
*@author王久印
* TODO
*/
@Component
publicclassScheduledTask2{
privateLogger logger = LoggerFactory.getLogger(ScheduledTask2.class);
privateintfixedDelayCount =1;
privateintfixedRateCount =1;
privateintinitialDelayCount =1;
privateintcronCount =1;
@Scheduled(fixedDelay =5000)//fixedDelay = 5000表示当前方法执行完毕5000ms后,Spring scheduling会再次调用该方法
publicvoidtestFixDelay{
logger.info(“===fixedDelay: 第{}次执行方法”, fixedDelayCount++);
}
@Scheduled(fixedRate =5000)//fixedRate = 5000表示当前方法开始执行5000ms后,Spring scheduling会再次调用该方法
publicvoidtestFixedRate{
logger.info(“===fixedRate: 第{}次执行方法”, fixedRateCount++);
}
@Scheduled(initialDelay =1000, fixedRate =5000)//initialDelay = 1000表示延迟1000ms执行第一次任务
publicvoidtestInitialDelay{
logger.info(“===initialDelay: 第{}次执行方法”, initialDelayCount++);
}
@Scheduled(cron =“0 0/1 * * * ?”)//cron接受cron表达式,根据cron表达式确定定时规则
publicvoidtestCron{
logger.info(“===initialDelay: 第{}次执行方法”, cronCount++);
}
}
使用 @Scheduled来创建定时任务 这个注解用来标注一个定时任务方法。
通过看 @Scheduled源码可以看出它支持多种参数:
cron:cron表达式,指定任务在特定时间执行;
fixedDelay:表示上一次任务执行完成后多久再次执行,参数类型为long,单位ms;
fixedDelayString:与fixedDelay含义一样,只是参数类型变为String;
fixedRate:表示按一定的频率执行任务,参数类型为long,单位ms;
fixedRateString: 与fixedRate的含义一样,只是将参数类型变为String;
initialDelay:表示延迟多久再第一次执行任务,参数类型为long,单位ms;
initialDelayString:与initialDelay的含义一样,只是将参数类型变为String;
zone:时区,默认为当前时区,一般没有用到。
importorg.springframework.boot.SpringApplication;
importorg.springframework.boot.autoconfigure.SpringBootApplication;
importorg.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
publicclassSpringBootCron2Application{
publicstaticvoidmain(String[] args){
SpringApplication.run(SpringBootCron2Application.class,args);
}
}
注:这里的 @EnableScheduling 注解,它的作用是发现注解 @Scheduled的任务并由后台执行。没有它的话将无法执行定时任务。
引用官方文档原文:
3、执行结果(单线程)@EnableScheduling ensures that a background task executor is created. Without it, nothing gets scheduled.
就完成了一个简单的定时任务模型,下面执行springBoot观察执行结果:
从控制台输入的结果中我们可以看出所有的定时任务都是在同一个线程池用同一个线程来处理的,那么我们如何来并发的处理各定时任务呢,请继续向下看。
4、多线程处理定时任务:
看到控制台输出的结果,所有的定时任务都是通过一个线程来处理的,我估计是在定时任务的配置中设定了一个 SingleThreadScheduledExecutor ,于是我看了源码,从 ScheduledAnnotationBeanPostProcessor 类开始一路找下去。果然,在 ScheduledTaskRegistrar (定时任务注册类)中的 ScheduleTasks 中又这样一段判断:
if(this.taskScheduler ==null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor;
this.taskScheduler =newConcurrentTaskScheduler(this.localExecutor);
}
这就说明如果 taskScheduler 为空,那么就给定时任务做了一个单线程的线程池,正好在这个类中还有一个设置 taskScheduler 的方法:
publicvoidsetScheduler(Object scheduler){
Assert.notNull(scheduler,“Scheduler object must not be null”);
if(schedulerinstanceofTaskScheduler) {
this.taskScheduler = (TaskScheduler) scheduler;
}
elseif(schedulerinstanceofScheduledExecutorService) {
this.taskScheduler =newConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
}
else{
thrownewIllegalArgumentException(“Unsupported scheduler type: “+ scheduler.getClass);
}
}
这样问题就很简单了,我们只需用调用这个方法显式的设置一个 ScheduledExecutorService 就可以达到并发的效果了。我们要做的仅仅是实现 SchedulingConfigurer 接口,重写 configureTasks 方法就OK了;
packagecom.accord.task;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.scheduling.annotation.SchedulingConfigurer;
importorg.springframework.scheduling.config.ScheduledTaskRegistrar;
importjava.util.concurrent.Executors;
/**
* 多线程执行定时任务
*@author王久印
*/
@Configuration
//所有的定时任务都放在一个线程池中,定时任务启动时使用不同都线程。
publicclassScheduleConfigimplementsSchedulingConfigurer{
@Override
publicvoidconfigureTasks(ScheduledTaskRegistrar taskRegistrar){
//设定一个长度10的定时任务线程池
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
}
}
5、执行结果(并发)
通过控制台输出的结果看出每个定时任务都是在通过不同的线程来处理了。
//////END//////
↓ 点击下方关注,看更多架构分享 ↓返回搜狐,查看更多
责任编辑:
声明:本文部分素材转载自互联网,如有侵权立即删除 。
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 精力有限,不少源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别
丞旭猿论坛
暂无评论内容