当前位置: 首页 > 科技观察

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

时间:2023-03-14 22:51:54 科技观察

在实际开发中,我们或多或少会使用定时任务来处理一些问题。例如,在财务项目对账中,每天定期核对昨天的账目,每月月初核对上个月的账目。比如我们需要处理一些旧的数据迁移,修复一些新项目和旧项目之间的数据不兼容问题等等。常规实现方案方案一:Timer目前在项目中使用较少,直接贴demo代码。具体介绍可以查看api,不过在一些框架中还是有用的。publicclassTestTimer{publicstaticvoidmain(String[]args){TimerTasktimerTask=newTimerTask(){@Overridepublicvoidrun(){System.out.println("任务运行:"+newDate());}};定时器timer=newTimer();//调度指定任务在指定时间开始重复固定延时执行。这是一个timer.schedule(timerTask,10,3000);}}每3秒执行一次结果:taskrun:SunDec1121:23:47CST2022taskrun:SunDec1121:23:50CST2022taskrun:SunDec1121:23:53CST2022这样,阿里代码检测插件会提示:从提示中可以看出,当多个线程在并行处理定时任务时,当Timer运行多个TimerTask时,只要其中一个没有捕获到异常抛出,其他任务会自动终止。方案二:ScheduledExecutorService和Timer类型,即阿里巴巴代码检查插件推荐的方案://参数:1.任务体2.第一次执行的延迟时间//3.任务执行间隔4.间隔时间单位service.scheduleAtFixedRate(()->System.out.println("taskScheduledExecutorService"+newDate()),0,3,时间单位.SECONDS);}}运行结果:taskScheduledExecutorServiceSunDec1121:30:06CST2022taskScheduledExecutorServiceSunDec1121:30:09CST2022taskScheduledExecutorServiceSunDec1121:30:12CST2022阿里巴巴代码检查插件也会提示:Thehint这是我们创建线程池的方式。建议我们手动创建线程池,而不是使用Executors工厂类,因为手动创建可以更有效地规划资源的使用。方案三:spring任务使用起来也很简单:@Slf4j@ComponentpublicclassScheduledService{@Scheduled(cron="0/5*****")publicvoidscheduled(){log.info("=====>>>>>使用cron{}",System.currentTimeMillis());}@Scheduled(fixedRate=5000)publicvoidscheduled1(){log.info("=====>>>>>使用fixedRate{}",System.currentTimeMillis());}@Scheduled(fixedDelay=5000)publicvoidscheduled2(){log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());}}运行结果:2022-12-1121:36:25.001INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>usingcron16707657850012022-12-1121:36:28.212INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用fixedRate16707657882122022-12-1121:36:28.212INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>fixedDelay16707657882122022-12-1121:36:30.001INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用cron16707657900012022-12-1121:36:33.212INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用fixedRate16707657932122022-12-1121:36:33.213INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>fixedDelay16707657932132022-12-1121:36:35.001INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用cron16707657950012022-12-1121:36:38.214INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用fixedRate16707657982142022-12-1121:36:38.214INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>fixedDelay16707657982142022-12-1121:36:40.001INFO10660---[SCheduling-1]com.tian.utils.ScheduledService:=====>>>>>使用cron16707658000012022-12-1121:36:43.214INFO10660---[scheduling-1]com.tian.utils。ScheduledService:=====>>>>>使用fixedRate16707658032142022-12-1121:36:43.215INFO10660---[scheduling-1]com.tian.utils.ScheduledService:=====>>>>>fixedDelay1670765803215方案四:多线程执行根据注解设置多线程定时任务:@Component@EnableScheduling//1.开启定时任务@EnableAsync//2.开启多线程publicclassMultithreadScheduleTask{@Async@Scheduled(fixedDelay=5000)//间隔5秒publicvoidfirst()throwsInterruptedException{System.out.println("第一个定时任务开始:"+LocalDateTime.now().toLocalTime()+"\r\nThread:"+Thread.currentThread().getName());System.out.println();线程.sleep(1000*10);}@Async@Scheduled(fixedDelay=5000)publicvoidsecond(){System.out.println("SectionTwoscheduledtasksstart:"+LocalDateTime.now().toLocalTime()+"\r\n线程:"+Thread.currentThread().getName());System.out.println();}}运行结果:第一个定时任务启动:21:44:02.800线程:存储操作日志记录表线程1第二个定时任务启动:21:44:02.801线程:存储操作日志记录表线程2第一个定时任务启动:21:44:07.801线程:Storage操作日志记录表线程3的第二个定时任务启动:21:44:07.802线程:storage操作日志记录表线程4第一个定时任务启动:21:44:12.807线程:storage运行日志记录表线程5第二个定时任务开始:21:44:12.812线程:存储运行日志记录表线程6...方案5:quartz我们需要引入依赖:org.springframework.bootspring-boot-starter-quartz实现类:publicclassMyquartzextendsQuartzJobBean{@OverrideprotectedvoidexecuteInternal(JobExecutionContextcontext)throwsJobExecutionException{System.out.println("这是我的石英计时任务");}}配置类:/***@authortianwc公众号:Java后端技术全栈,面试专栏*@version1.0.0*@date2022年12月11日21:48*/@ConfigurationpublicclassQuartzConfig{@BeanpublicJobDetailteatQuartzDetail(){返回JobBuilder.newJob(MyQuartz.class).withIdentity("myQuartz").storeDurably().build();}@BeanpublicTriggertestQuartzTrigger(){SimpleScheduleBuilderscheduleBuilder=SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(10)//设置时间循环单位为秒.repeatForever();返回TriggerBuilder.newTrigger().forJob(teatQuartzDetail()).withIdentity("testQuartz").withSchedule(scheduleBuilder).build();只要启动SpringBoot项目,就会输出:ThisismyquartztimedtaskThisismyquartztimertaskThisismyquartztimetask我们在项目中的其他解决方案可能涉及动态调整定时任务执行核心表达式,动态关闭定时任务,我们可以使用SchedulingConfigurer来实现(使用数据库组合):例如:@ConfigurationpublicclassScheduledConfigimplementsSchedulingConfigurer{@AutowiredprivateApplicationContextcontext;@OverridepublicvoidconfigureTasks(ScheduledTaskRegistrartaskRegistrar){for(SpringScheduledCronspringRegistrar){for(SpringScheduledCron)springRepoScheduledC拉斯克拉兹;对象任务;尝试{clazz=Class.forName(springScheduledCron.getCronKey());task=context.getBean(clazz);}catch(ClassNotFoundExceptione){thrownewIllegalArgumentException("spring_scheduled_cron表数据"+springScheduledCron.getCronKey()+"不正确",e);}catch(BeansExceptione){thrownewIllegalArgumentException(springScheduledCron.getCronKey()+"不包含在spring管理中",e);}Assert.isAssignable(ScheduledOfTask.class,task.getClass(),"定时任务类必须实现ScheduledOfTask接口");//可以通过改变数据库数据来动态改变执行周期taskRegistrar.addTriggerTask(((Runnable)task),triggerContext->{//这个可以使用持久层,比如Mybatis,获取StringcronExpression="0/10****?"从数据库中返回新的CronTrigger(cronExpression).nextExecutionTime(触发上下文);});}}@BeanpublicExecutortaskExecutor(){returnExecutors.newScheduledThreadPool(10);如果你的项目中能找到类似的东西,你可以在线搜索SchedulingConfigurer,然后展开。来到分布式任务调度。什么是分布式任务调度?任务调度是指根据给定的时间点、给定的时间间隔或给定的执行次数自动执行任务。任务调度是操作系统的重要组成部分,对于实时操作系统而言,任务调度直接影响操作系统的实时性。任务调度涉及到多线程并发、运行时规则定制分析、线程池维护等诸多方面的工作。当WEB服务器接受一个请求时,它会创建一个新的线程服务。但是,资源是有限的,必须控制资源。首先,限制最大服务线程数。其次,考虑与线程池共享服务线程资源,减少频繁创建和销毁线程的消耗。然后,任务调度信息的存储包括运行时间、调度规则和运行数据等。一个合适的任务调度框架对项目的整体性能非常重要。分布式任务调度框架有:cronsun、Elastic-job、saturn、lts、TBSchedule、xxl-job等,另外就是cron表达式。建议到http://www.pppet.net/手动选择,根据自己的业务情况自动生成表达式。