5分钟搞定,实现 定时任务 的五种方案!

我们在实际开发中,多多少少都会用到定时任务来处理一些问题。

比如金融项目中的对账,每天定时对昨天的账务进行核对,每个月初对上个月的账务进行核对等。

还比如,我们需要处理一些老数据迁移,修复一些新项目和老项目数据不兼容的问题等等。

常规实现方案方案1:Timer

这个目前在项目中用得较少,直接贴demo代码。

具体的介绍可以查看api,但是在某些框架中是有用到。

publicclassTestTimer{publicstaticvoidmain(String[]args){TimerTasktimerTask=newTimerTask(){@Overridepublicvoidrun(){("taskrun:"+newDate());}};Timertimer=newTimer();//安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次(timerTask,10,3000);}}

执行结果:

taskrun:SunDec1121:23:47CST2022taskrun:SunDec1121:23:50CST2022taskrun:SunDec1121:23:53CST2022

这么使用,阿里代码检查插件会提示:

从提示中可以看出,在多线程并行处理定时任务时,Timer运行多个TimerTask时,只要有其中之一没有捕获抛出的异常,其他任务会自动终止运行。

方案2:ScheduledExecutorService

和Timer类型,也就是阿里代码检查插件推荐的方案:

publicclassTestScheduledExecutorService{publicstaticvoidmain(String[]args){ScheduledExecutorServiceservice=();//参数:1、任务体2、首次执行的延时时间//3、任务执行间隔4、间隔时间单位(()-("taskScheduledExecutorService"+newDate()),0,3,);}}

运行结果:

taskScheduledExecutorServiceSunDec1121:30:06CST2022taskScheduledExecutorServiceSunDec1121:30:09CST2022taskScheduledExecutorServiceSunDec1121:30:12CST2022

阿里代码检查插件也会提示:

这里提示的是我们创建线程池的方式,建议我们使用手动创建线程池,不要使用Executors工厂类,因为手动创建更能有效规划资源的使用。

方案3:springtask

用起来也非常简单:

@Slf4j@ComponentpublicclassScheduledService{@Scheduled(cron="0/5*****")publicvoidscheduled(){("=====使用cron{}",());}@Scheduled(fixedRate=5000)publicvoidscheduled1(){("=====使用fixedRate{}",());}@Scheduled(fixedDelay=5000)publicvoidscheduled2(){("=====fixedDelay{}",());}}

运行结果:

2022-12-1121:36:25.001INFO10660---[scheduling-1]:=====使用cron16707657850012022-12-1121:36:28.212INFO10660---[scheduling-1]:=====使用fixedRate16707657882122022-12-1121:36:28.212INFO10660---[scheduling-1]:=====fixedDelay16707657882122022-12-1121:36:30.001INFO10660---[scheduling-1]:=====使用cron16707657900012022-12-1121:36:33.212INFO10660---[scheduling-1]:=====使用fixedRate16707657932122022-12-1121:36:33.213INFO10660---[scheduling-1]:=====fixedDelay16707657932132022-12-1121:36:35.001INFO10660---[scheduling-1]:=====使用cron16707657950012022-12-1121:36:38.214INFO10660---[scheduling-1]:=====使用fixedRate16707657982142022-12-1121:36:38.214INFO10660---[scheduling-1]:=====fixedDelay16707657982142022-12-1121:36:40.001INFO10660---[scheduling-1]:=====使用cron1670765-12-1121:36:43.214INFO10660---[scheduling-1]:=====使用fixedRate16707658032142022-12-1121:36:43.215INFO10660---[scheduling-1]:=====fixedDelay1670765803215
方案4:多线程执行

基于注解设定多线程定时任务:

@Component@EnableScheduling//1.开启定时任务@EnableAsync//2.开启多线程publicclassMultithreadScheduleTask{@Async@Scheduled(fixedDelay=5000)//间隔5秒publicvoidfirst()throwsInterruptedException{("第一个定时任务开始:"+().toLocalTime()+"\r\n线程:"+().getName());();(1000*10);}@Async@Scheduled(fixedDelay=5000)publicvoidsecond(){("第二个定时任务开始:"+().toLocalTime()+"\r\n线程:"+().getName());();}}

运行结果:

第一个定时任务开始:21:44:02.800线程:入库操作日志记录表线程1第二个定时任务开始:21:44:02.801线程:入库操作日志记录表线程2第一个定时任务开始:21:44:07.801线程:入库操作日志记录表线程3第二个定时任务开始:21:44:07.802线程:入库操作日志记录表线程4第一个定时任务开始:21:44:12.807线程:入库操作日志记录表线程5第二个定时任务开始:21:44:12.812线程:入库操作日志记录表线程6
方案5:quartz

我们需要引入依赖:

/groupIdartifactIdspring-boot-starter-quartz/artifactId/depency

实现类:

publicclassMyquartzextsQuartzJobBean{@OverrideprotectedvoidexecuteInternal(JobExecutionContextcontext)throwsJobExecutionException{("这是我的quartz定时任务");}}

配置类:

/***@authortianwc面试专栏*@*@date2022年12月11日21:48*/@ConfigurationpublicclassQuartzConfig{@BeanpublicJobDetailteatQuartzDetail(){().withIdentity("myQuartz").storeDurably().build();}@BeanpublicTriggertestQuartzTrigger(){SimpleScheduleBuilderscheduleBuilder=().withIntervalInSeconds(10)//设置时间周期单位秒.repeatForever();().forJob(teatQuartzDetail()).withIdentity("testQuartz").withSchedule(scheduleBuilder).build();}}

只要启动SpringBoot项目,就会输出:

这是我的quartz定时任务这是我的quartz定时任务这是我的quartz定时任务
其他方案

我们在项目,可能会涉及动态调整定时任务执行core表达式、动态关闭开启定时任务,我们可以使用SchedulingConfigurer来实现(使用数据库结合来搞):

比如:

@ConfigurationpublicclassScheduledConfigimplementsSchedulingConfigurer{@AutowiredprivateApplicationContextcontext;@OverridepublicvoidconfigureTasks(ScheduledTaskRegistrartaskRegistrar){for(SpringScheduledCronspringScheduledCron:()){Class?clazz;Objecttask;try{clazz=(());task=(clazz);}catch(ClassNotFoundExceptione){thrownewIllegalArgumentException("spring_scheduled_cron表数据"+()+"有误",e);}catch(BeansExceptione){thrownewIllegalArgumentException(()+"未纳入到spring管理",e);}(,(),"定时任务类必须实现ScheduledOfTask接口");//可以通过改变数据库数据进而实现动态改变执行周期(((Runnable)task),triggerContext-{//这个可以使用持久层,比如Mybatis来实现,从数据库中获取StringcronExpression="0/10****?"returnnewCronTrigger(cronExpression).nextExecutionTime(triggerContext);});}}@BeanpublicExecutortaskExecutor(){(10);}}

如果项目中用得到类似的,可以网上搜搜SchedulingConfigurer便可实现。

进而再扩展,那就来到分布式任务调度了。

什么是分布式任务调度?

任务调度是指基于给定的时间点,给定的时间间隔或者给定执行次数自动得执行任务。任务调度是是操作系统的重要组成部分,而对于实时的操作系统,任务调度直接影响着操作系统的实时性能。任务调度涉及到多线程并发、运行时间规则定制及解析、线程池的维护等诸多方面的工作。

WEB服务器在接受请求时,会创建一个新的线程服务。但是资源有限,必须对资源进行控制,首先就是限制服务线程的最大数目,其次考虑以线程池共享服务的线程资源,降低频繁创建、销毁线程的消耗;然后任务调度信息的存储包括运行次数、调度规则以及运行数据等。一个合适的任务调度框架对于项目的整体性能来说显得尤为重要。

分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job等。

另外,就是cron表达式,推荐

可以根据自己业务情况来,手动选择,自动生成表达式。

好了,今天就分享这么多。

版权声明:本站所有作品(图文、音视频)均由用户自行上传分享,仅供网友学习交流,不声明或保证其内容的正确性,如发现本站有涉嫌抄袭侵权/违法违规的内容。请举报,一经查实,本站将立刻删除。

相关推荐