zoukankan      html  css  js  c++  java
  • spring自带的定时任务功能@EnableScheduling

    1 demo

    package com.test.domi.config;
     
    import org.springframework.beans.factory.annotation.Configurable;
    import org.springframework.scheduling.annotation.EnableScheduling;
    import org.springframework.scheduling.annotation.Scheduled;
    import org.springframework.stereotype.Component;
    import java.text.SimpleDateFormat;
    import java.util.Date;
     
    @Component
    @Configurable
    @EnableScheduling
    public class ScheduledTasks {
        //每30秒执行一次
        @Scheduled(fixedRate = 1000 30)
        public void reportCurrentTime(){
            System.out.println ("Scheduling Tasks Examples: The time is now " + dateFormat ().format (new Date ()));
        }
     
        //在固定时间执行
        @Scheduled(cron = "0 */1 *  * * * ")
        public void reportCurrentByCron(){
            System.out.println ("Scheduling Tasks Examples By Cron: The time is now " + dateFormat ().format (new Date()));
        }
     
        private SimpleDateFormat dateFormat(){
            return new SimpleDateFormat ("HH:mm:ss");
        }
    Scheduling Tasks Examples: The time is now 11:55:54
    Scheduling Tasks Examples By Cron: The time is now 11:56:00
    Scheduling Tasks Examples: The time is now 11:56:24
    Scheduling Tasks Examples: The time is now 11:56:54
    Scheduling Tasks Examples By Cron: The time is now 11:57:00

    2 详解

    http://tramp.cincout.cn/2017/08/18/spring-task-2017-08-18-spring-boot-enablescheduling-analysis/

    cron表达式:https://www.zhyd.me/article/43

    1.cron是设置定时执行的表达式,如 0 0/5 * * * ?每隔五分钟执行一次

    2.zone表示执行时间的时区

    3.fixedDelay 和fixedDelayString 一个固定延迟时间执行,上个任务完成后,延迟多久执行

    4.fixedRate 和fixedRateString一个固定频率执行,上个任务开始后多长时间后开始执行

    5.initialDelay 和initialDelayString表示一个初始延迟时间,第一次被调用前延迟的时间

    3 总结常见问题

    a: 单线程任务丢失,转为异步线程池

    默认的 ConcurrentTaskScheduler 计划执行器采用Executors.newSingleThreadScheduledExecutor() 实现单线程的执行器。因此,对同一个调度任务的执行总是同一个线程。如果任务的执行时间超过该任务的下一次执行时间,则会出现任务丢失,跳过该段时间的任务。上述问题有以下解决办法:

    采用异步的方式执行调度任务,配置 Spring 的 @EnableAsync,在执行定时任务的方法上标注 @Async配置任务执行池,线程池大小 n 的数量为 单个任务执行所需时间 / 任务执行的间隔时间。如下:


    //每30秒执行一次
       @Async("taskExecutor")
       @Scheduled(fixedRate = 1000 3)
       public void reportCurrentTime(){
           System.out.println ("线程" + Thread.currentThread().getName() + "开始执行定时任务===&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&7&&&====》"
                   new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
           long start = System.currentTimeMillis();
           Future<Boolean> isOk1;
           Future<Boolean> isOk2;
      。 。。。。。。。。。省略。。。。。。。
     

    b: 关于分布式情况下,重复执行的问题(两种方案)

    1:可以使用redis的分布式锁保证spring schedule集群只执行一次。 redis分布式锁是通过setnx命令实现的。该命令的作用是,当往redis中存入一个值时,会先判断该值对应的key是否存在,如果存在则返回0,如果不存在,则将该值存入redis并返回1。(但是在分布式跨时区部署的时候,依然无法避免重复执行)

    @Component
    @Configuration
    @EnableScheduling
    public class AutoConvertTask {
        private static final Logger logger = LoggerFactory.getLogger(AutoConvertTask.class);
     
        @Autowired
        private RedisTemplate redisTemplate;
     
        private static final String LOCK = "task-job-lock";
     
        private static final String KEY = "tasklock";
     
        @Scheduled(cron = "0 0 0 * * ? ")
        public void autoConvertJob() {
            boolean lock = false;
            try {
                lock = redisTemplate.opsForValue().setIfAbsent(KEY, LOCK);
                logger.info("是否获取到锁:" + lock);
                if (lock) {
                    List<GameHistory> historyList = historyService.findTenDaysAgoUntreated();
                    for (GameHistory history : historyList) {
                        update(history);
                    }
                else {
                    logger.info("没有获取到锁,不执行任务!");
                    return;
                }
            finally {
                if (lock) {
                    redisTemplate.delete(KEY);
                    logger.info("任务结束,释放锁!");
                else {
                    logger.info("没有获取到锁,无需释放锁!");
                }
            }
     
        }
     
    }

    2:可以通过使用shedlock将spring schedule上锁。详细见:https://segmentfault.com/a/1190000011975027

    c: 服务器宕机之后,丢失的任务如何补偿? 

    可以将每次的任务执行时间缓在redis里,下次执行任务的时候都取出该时间,判断是否为上一个周期,如果不是,可以计算出中间丢失的周期数,然后做响应的补偿操作。如果怕redis宕机,可以将“执行时间”持久化到表中。

  • 相关阅读:
    HDU 1394Minimum Inversion Number 数状数组 逆序对数量和
    2016中国大学生程序设计竞赛(长春)-重现赛 1010Ugly Problem 回文数 模拟
    Codeforces 723C. Polycarp at the Radio 模拟
    hihoCode 1078 : 线段树的区间修改
    hihocode 1077 : RMQ问题再临-线段树
    POJ 2352Stars 树状数组
    Codeforces 714C. Sonya and Queries Tire树
    Codeforces 710C. Magic Odd Square n阶幻方
    Codeforces 709C 模拟
    ICM Technex 2017 and Codeforces Round #400 (Div. 1 + Div. 2, combined) D. The Door Problem 2-SAT
  • 原文地址:https://www.cnblogs.com/telwanggs/p/11280318.html
Copyright © 2011-2022 走看看