zoukankan      html  css  js  c++  java
  • 理解Spring定时任务@Scheduled的两个属性fixedRate和fixedDelay

    fixedRate和fixedDelay都是表示任务执行的间隔时间

    fixedRate和fixedDelay的区别:
    fixedDelay非常好理解,它的间隔时间是根据上次的任务结束的时候开始计时的。比如一个方法上设置了fixedDelay=5*1000,那么当该方法某一次执行结束后,开始计算时间,当时间达到5秒,就开始再次执行该方法。

    fixedRate理解起来比较麻烦,它的间隔时间是根据上次任务开始的时候计时的。比如当方法上设置了fiexdRate=5*1000,该执行该方法所花的时间是2秒,那么3秒后就会再次执行该方法。
    但是这里有个坑,当任务执行时长超过设置的间隔时长,那会是什么结果呢。打个比方,比如一个任务本来只需要花2秒就能执行完成,我所设置的fixedRate=5*1000,但是因为网络问题导致这个任务花了7秒才执行完成。当任务开始时Spring就会给这个任务计时,5秒钟时候Spring就会再次调用这个任务,可是发现原来的任务还在执行,这个时候第二个任务就阻塞了(这里只考虑单线程的情况下,多线程后面再讲),甚至如果第一个任务花费的时间过长,还可能会使第三第四个任务被阻塞。被阻塞的任务就像排队的人一样,一旦前一个任务没了,它就立马执行。

    下面用代码来具体验证一下。

    @SpringBootApplication
    @EnableScheduling
    public class ScheduledemoApplication {
    
        private AtomicInteger number = new AtomicInteger();
    
        public static void main(String[] args) {
            SpringApplication.run(ScheduledemoApplication.class, args);
        }
    
    
        @Scheduled(fixedRate = 5000 )
        public void job(){
    
            LocalTime start = LocalTime.now();
            //前面和末尾几个字符串是用来改变打印的颜色的
            System.out.println("33[31;4m" + Thread.currentThread() + " start " + number.incrementAndGet()
                    + " @ " + start + "33[0m");
            try {
                Thread.sleep(ThreadLocalRandom.current().nextInt(15)*1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            LocalTime end = LocalTime.now();
    
            System.out.println(Thread.currentThread() + " end " + number.get() + " @ "+
                     end + ", seconds cost "+ (ChronoUnit.SECONDS.between(start, end)));
    
        }
    
    }

    执行结果如下:

    可以看到任务第一次执行完以后还有间隔4秒才执行第二次,可是到了第二次结束的时候,就没有间隔了,直接就执行第三次了。甚至在执行第四次的时候,明明第四次任务只花费了1秒,可还是马上就执行第五次了。因为前面两次任务花费的时间太久了,有好几个被调度的任务都被阻塞了。所以当第四次一结束,马上第五次就执行了。

    @Scheduled(fixedRate)如何避免任务被阻塞
    答案是加上注解@EnableAsync(类上)和@Async(方法上),加了注解以后,就开启了多线程模式,当到了下一次任务的执行时机时,如果上一次任务还没执行完,就会自动创建一个新的线程来执行它。异步执行也可以理解为保证了任务以固定速度执行。
    开启多线程后执行结果如下:

    可以看到,开启多线程后,每次任务开始的间隔都是5秒钟。这是符合我们预期的,但是最后还有点缺陷,这种情况下的线程是随着任务一执行完就销毁的,等下次有需要了程序再创建一个。每次都要重新创建明显是太影响性能了,所以需要在代码里给他一个线程池。

    创建一个线程池

    @Bean
        public TaskScheduler taskScheduler() {
            ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
            taskScheduler.setPoolSize(5);
            return taskScheduler;
        }

    可以看到现在程序就不会再自己创建线程了,每次都会从线程池里面拿。需注意的是,如果线程池里的所有线程都被拿去执行调度任务了,且又到了时间要执行一次任务,那么这个任务又会被阻塞。所以实际开发中如果想要保证任务以速度被执行,线程池的最大线程数量可要想好。

    话说真的要深入理解的话其实还得看源码,这些测试只是通过表象来猜测。
    over

  • 相关阅读:
    leetcode 155. Min Stack 、232. Implement Queue using Stacks 、225. Implement Stack using Queues
    leetcode 557. Reverse Words in a String III 、151. Reverse Words in a String
    leetcode 153. Find Minimum in Rotated Sorted Array 、154. Find Minimum in Rotated Sorted Array II 、33. Search in Rotated Sorted Array 、81. Search in Rotated Sorted Array II 、704. Binary Search
    leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String
    leetcode 162. Find Peak Element
    leetcode 88. Merge Sorted Array
    leetcode 74. Search a 2D Matrix 、240. Search a 2D Matrix II
    Android的API版本和名称对应关系
    spring 定时任务执行两次解决办法
    解析字符串为泛型的方法
  • 原文地址:https://www.cnblogs.com/wpcnblog/p/11981391.html
Copyright © 2011-2022 走看看