首页 > 其他分享 >5分钟搞定,实现 定时任务 的五种方案!

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

时间:2023-06-11 23:05:08浏览次数:41  
标签:11 搞定 21 五种 线程 2022 定时 public


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

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

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


常规实现方案

方案1:Timer

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

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

public class TestTimer {

    public static void main(String[] args) {

        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("task  run:"+ new Date());
            }
        };

        Timer timer = new Timer();
        //安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次
        timer.schedule(timerTask,10,3000);
    }
}

执行结果:

task  run:Sun Dec 11 21:23:47 CST 2022
task  run:Sun Dec 11 21:23:50 CST 2022
task  run:Sun Dec 11 21:23:53 CST 2022

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

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

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

方案2:ScheduledExecutorService

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

public class TestScheduledExecutorService {

    public static void main(String[] args) {

        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        // 参数:1、任务体 2、首次执行的延时时间
        // 3、任务执行间隔 4、间隔时间单位
        service.scheduleAtFixedRate(()->System.out.println("task ScheduledExecutorService "+new Date()), 0, 3, TimeUnit.SECONDS);
    }
}

运行结果:

task ScheduledExecutorService Sun Dec 11 21:30:06 CST 2022
task ScheduledExecutorService Sun Dec 11 21:30:09 CST 2022
task ScheduledExecutorService Sun Dec 11 21:30:12 CST 2022

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

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

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

方案3:spring task

用起来也非常简单:

@Slf4j
@Component
public class ScheduledService {

    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled(){
        log.info("=====>>>>>使用cron  {}",System.currentTimeMillis());
    }

    @Scheduled(fixedRate = 5000)
    public void scheduled1() {
        log.info("=====>>>>>使用fixedRate{}", System.currentTimeMillis());
    }

    @Scheduled(fixedDelay = 5000)
    public void scheduled2() {
        log.info("=====>>>>>fixedDelay{}",System.currentTimeMillis());
    }

}

运行结果:

2022-12-11 21:36:25.001  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用cron  1670765785001
2022-12-11 21:36:28.212  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用fixedRate1670765788212
2022-12-11 21:36:28.212  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>fixedDelay1670765788212
2022-12-11 21:36:30.001  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用cron  1670765790001
2022-12-11 21:36:33.212  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用fixedRate1670765793212
2022-12-11 21:36:33.213  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>fixedDelay1670765793213
2022-12-11 21:36:35.001  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用cron  1670765795001
2022-12-11 21:36:38.214  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用fixedRate1670765798214
2022-12-11 21:36:38.214  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>fixedDelay1670765798214
2022-12-11 21:36:40.001  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用cron  1670765800001
2022-12-11 21:36:43.214  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>使用fixedRate1670765803214
2022-12-11 21:36:43.215  INFO 10660 --- [   scheduling-1] com.tian.utils.ScheduledService          : =====>>>>>fixedDelay1670765803215

方案4:多线程执行

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

@Component
@EnableScheduling   // 1.开启定时任务
@EnableAsync        // 2.开启多线程
public class MultithreadScheduleTask {

    @Async
    @Scheduled(fixedDelay = 5000)  //间隔5秒
    public void first() throws InterruptedException {
        System.out.println("第一个定时任务开始 : " + LocalDateTime.now().toLocalTime() + "\r\n线程 : " + Thread.currentThread().getName());
        System.out.println();
        Thread.sleep(1000 * 10);
    }

    @Async
    @Scheduled(fixedDelay = 5000)
    public void second() {
        System.out.println("第二个定时任务开始 : " + 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
线程 : 入库操作日志记录表 线程3

第二个定时任务开始 : 21:44:07.802
线程 : 入库操作日志记录表 线程4

第一个定时任务开始 : 21:44:12.807
线程 : 入库操作日志记录表 线程5

第二个定时任务开始 : 21:44:12.812
线程 : 入库操作日志记录表 线程6
......

方案5:quartz

我们需要引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

实现类:

public class Myquartz extends QuartzJobBean {
    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        System.out.println("这是我的 quartz 定时任务");
    }
}

配置类:

/**
 * @author tianwc  公众号:java后端技术全栈、面试专栏
 * @version 1.0.0
 * @date 2022年12月11日 21:48
 */
@Configuration
public class QuartzConfig {

    @Bean
    public JobDetail teatQuartzDetail(){
        return JobBuilder.newJob(MyQuartz.class).withIdentity("myQuartz").storeDurably().build();
    }

    @Bean
    public Trigger testQuartzTrigger(){
        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()

                .withIntervalInSeconds(10)  //设置时间周期单位秒
                .repeatForever();

        return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())

                .withIdentity("testQuartz")
                .withSchedule(scheduleBuilder)
                .build();
    }
}

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

这是我的 quartz 定时任务
这是我的 quartz 定时任务
这是我的 quartz 定时任务

其他方案

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

比如:

@Configuration
public class ScheduledConfig implements SchedulingConfigurer {
    @Autowired
    private ApplicationContext context; 
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        for (SpringScheduledCron springScheduledCron : cronRepository.findAll()) {
            Class<?> clazz;
            Object task;
            try {
                clazz = Class.forName(springScheduledCron.getCronKey());
                task = context.getBean(clazz);
            } catch (ClassNotFoundException e) {
                throw new IllegalArgumentException("spring_scheduled_cron表数据" + springScheduledCron.getCronKey() + "有误", e);
            } catch (BeansException e) {
                throw new IllegalArgumentException(springScheduledCron.getCronKey() + "未纳入到spring管理", e);
            }
            Assert.isAssignable(ScheduledOfTask.class, task.getClass(), "定时任务类必须实现ScheduledOfTask接口");
            // 可以通过改变数据库数据进而实现动态改变执行周期
            taskRegistrar.addTriggerTask(((Runnable) task),
                    triggerContext -> {
                        //这个可以使用持久层,比如Mybatis来实现,从数据库中获取
                        String cronExpression = "0/10 * * * * ? "                       
                        return new CronTrigger(cronExpression).nextExecutionTime(triggerContext);
                    }
            );
        }
    }
    @Bean
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(10);
    }
}

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

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

什么是分布式任务调度?

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

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

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

另外,就是cron表达式,推荐 http://www.pppet.net/

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

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


标签:11,搞定,21,五种,线程,2022,定时,public
From: https://blog.51cto.com/u_11702014/6459225

相关文章

  • 30分钟内搞定 50张表的 CRUD,666!
    大家好,我是田哥前面我跟大家分享小伙伴正在用它来练练手,其中,有个非常聪明的小伙伴发现了个问题:大量类似的代码,差不多一样的CRUD.医院项目里确实存在大量的CRUD,其实嘛,医院项目毕竟还是重点偏向于后台管理系统,不过,我前段时间增加了外网预约挂号功能,但再怎么说,也还是偏向于后台,比较主......
  • [TSG开发日志2]串口通信?VS下FaroSDK编译环境?一文搞定
    艹,不知道为什么我之前写的法如sdk没有保存,总而言之就是莫名其妙整个工程没有了,后来我想了想,也有可能就是我自己删掉了,因为在配置法如工程的时候遇到了一些小问题,不过现在也解决了。一、关于串口通信:Qt的确有自己的串口通信类,就是QSerialPort,但是我们在使用过程中因为要更加定制......
  • python定时任务
      无论哪种编程语言,时间肯定都是非常重要的部分,今天来看一下python如何来处理时间和python定时任务,注意咯:本篇所讲是python3版本的实现,在python2版本中的实现略有不同,有时间会再写一篇以便大家区分。1.计算明天和昨天的日期#!/usr/bin/envpython#coding=utf-8#获取今天、......
  • Mysql文章大汇总,一文全搞定!!!
    一次MySQL千万级大表的优化过程开发人员必备的9大MySQL索引和查询优化mysql性能优化之一【索引--基础】mysql性能优化一mysql性能优化二高级版MySQL优化方案常见mysql的慢查询优化方式MySQL性能优化之骨灰级,高阶神技!史上最全的MySQL高性能优化实战总结!Spring-boot+Mybatis+Maven......
  • 1327. 列出指定时间段内所有的下单产品
    【题目】表:Products+------------------+---------+|ColumnName     |Type   |+------------------+---------+|product_id      |int    ||product_name    |varchar||product_category|varchar|+------------------+---------+produc......
  • 用Python白嫖WPS付费功能:把PPT转为 1张 长图,1行代码搞定
    大家好,这里是程序员晚枫,小红薯也叫这个名。读者群......
  • Golang 定时任务 github/robfig/cron/v3
    robfig/cron/v3 是一个Golang的定时任务库,支持cron表达式。低耦合高内聚,其中涉及装饰器模式,并发处理等。packagemainimport"github.com/robfig/cron/v3"c:=cron.New()//添加一个任务,每30分钟执行一次c.AddFunc("30****",func(){fmt.Println("Everyho......
  • Python实现定时自动关闭的tkinter窗口
    功能简要说明:程序运行后10秒钟自动关闭。技术要点:tkinter应用程序的destroy()方法,多线程编程。代码截图:运行效果:......
  • linux 定时任务
    可以用非root用户创建定时任务Linuxcrontab是用来定期执行程序的命令。当安装完成操作系统之后,默认便会启动此任务调度命令。crond命令每分钟会定期检查是否有要执行的工作,如果有要执行的工作便会自动执行该工作。注意:新创建的cron任务,不会马上执行,至少要过2分钟后才可......
  • redis通用命令及其五种基本数据结构
    Redis通用命令介绍:KEYS:查看符合模版的所有key,DEL:删除一个指定的KEYEXISTS:判断KEY是否存在EXPIRE:给一个key设置有效期,有效期到期时该KEY会自动删除TTL:查看一个key到剩余有效期示例:127.0.0.1:6379>existstest_key(integer)1127.0.0.1:6379>expire......