Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别

Posted 请叫我大师兄_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别相关的知识,希望对你有一定的参考价值。

Java实现定时任务,一般都是用一个线程,设置个时间,让他定时执行,注意力一般都是集中在这个线程的实现,很少考虑到具体定时执行线程的这个过程。scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别大吗?要是不了解的话,还真会因为不了解两者的差别导致一些线上bug呢!

正规的做法,咱要使用线程,就得正规的使用线程池来实现线程的调用,而不能图一时之快,手动的弄个线程就给他start了,这么操作是不妥当,坚决反对。而创建线程池又有讲究,期望自己手动设置一些参数,从而了解和牢记线程池实现原理。

定时任务线程池提供类

public final class MonitorService 
    private static final int CORE_POOL_SIZE = 10;
    private static final int DELAY = 60;
    private static volatile ScheduledExecutorService monitorExecutor;

    public static ScheduledExecutorService getMonitorExecutor() 
        if (monitorExecutor == null) 
            synchronized (MonitorService.class) 
                if (monitorExecutor == null) 
                    ThreadFactory monitorThreadFactory = new ThreadFactoryBuilder().setNameFormat("monitorThreadPool-%d").build();
                    monitorExecutor = new ScheduledThreadPoolExecutor(CORE_POOL_SIZE, monitorThreadFactory, new ThreadPoolExecutor.AbortPolicy());
                
            
        
        return monitorExecutor;
    

问题0:这个线程池的maximumPoolSize是多少?

super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue(), threadFactory, handler)

问题1:提交的任务超过 corePoolSize 会怎么样?

    @Test
    public void test() throws InterruptedException 
        for (int i = 0; i < 100; i++) 
            int finalI = i;
            MonitorService.getMonitorExecutor().scheduleWithFixedDelay(()->
                System.out.println(finalI + "    ");
                try 
                    TimeUnit.MINUTES.sleep(5);
                 catch (Exception ignore) 
                
            , 5, 5, TimeUnit.SECONDS);
        
        System.out.println("主线程运行到此。");
        TimeUnit.MINUTES.sleep(5);
    

运行结果截图:

结论:

他首先会把多余的给放到缓存队列中,队列放不下了,才会启动大于core size 个的线程去执行任务,当超过maximumPoolSize 的时候,执行拒绝策略。

问题2:上面的情况为什么不直接new线程去执行任务,而是把任务丢到队列里等着?

就好比一个国企里面有10个(core)正式工的名额,最多招10个正式工,要是任务超过正式工人数(task > core)的情况下,工厂领导(线程池)不是首先扩招工人,还是这10人,但是任务可以稍微积压一下,即先放到队列去(代价低)。10个正式工慢慢干,迟早会干完的,要是任务还在继续增加,超过正式工的加班忍耐极限了(队列满了),就的招临时工帮忙了(注意是临时工)要是正式工加上临时工还是不能完成任务,那新来的任务就会被领导拒绝了(线程池的拒绝策略)。
为啥老板不上来直接招正式工?
首先,招人(new thread)是不是得走流程,办手续,耗时耗力;
其次,要是任务量降低了,养多出来的几个人没活干成本高(维护更多的线程的成本);
最后,要辞退临时工也得各种走流程,各种赔偿,得不偿失。(超时清掉产能过剩的线程的成本)。
正式工(线程)相对于计算机世界,是稀缺资源。国企的铁饭碗可不就是稀缺资源嘛,不到不得已,是不能泛滥的。

问题3:两个定时任务的实现的差别?

1,先通过实际代码看scheduleWithFixedDelay的实际运行效果。

    @Test
    public void with() throws InterruptedException 
        System.out.println("当前是:" + TimeUtils.now());
        MonitorService.getMonitorExecutor().scheduleWithFixedDelay(() -> 
            try 
                System.out.println("线程 run 了。。。。。。" + TimeUtils.nowS());
                TimeUnit.SECONDS.sleep(5);
                System.out.println("线程 sleep 完了。。。。。。" + TimeUtils.nowS());
             catch (InterruptedException e) 
                e.printStackTrace();
            
        , 1, 1, TimeUnit.SECONDS);
        System.out.println("主线程运行到此。当前是:" + TimeUtils.now());
        TimeUnit.MINUTES.sleep(500);
    

运行结果截图:

定时任务执行过程中睡了5s,41开始,46结束,下次47的时候开始,我们设置的执行频率是1s一执行,但是,实际执行频率是6s一次。

理论:

scheduleWithFixedDelay 指的是“以固定的延时”执行,delay(延时)指的是一次执行终止和下一次执行开始之间的延迟。本test现象:执行完歇息1秒,再下次执行 。

痛点:run的执行多少总要耽误点时间的,即使是毫秒级别的,但是,执行的次数多了,这个定时任务的延迟频率会越来越大。

2,再通过实际代码看scheduleAtFixedRate的实际运行效果。

    @Test
    public void at() throws InterruptedException 
        System.out.println("当前是:" + TimeUtils.now());
        MonitorService.getMonitorExecutor().scheduleAtFixedRate(() -> 
            try 
                System.out.println("线程 run 了。。。。。。" + TimeUtils.nowS());
                TimeUnit.SECONDS.sleep(5);
                System.out.println("线程 sleep 完了。。。。。。" + TimeUtils.nowS());
             catch (InterruptedException e) 
                e.printStackTrace();
            
        , 1, 1, TimeUnit.SECONDS);
        System.out.println("主线程运行到此。当前是:" + TimeUtils.now());
        TimeUnit.MINUTES.sleep(500);
    

运行结果截图:

96开始执行,睡5s,01结束,然后,下一次也在01的时候就开始了 

理论:

scheduleAtFixedRate 指的是“以固定的频率”执行,period(周期)指的是两次成功执行之间的时间。 上一个任务开始的时间计时,一个period后,检测上一个任务是否执行完毕,如果上一个任务执行完毕,则当前任务立即执行,如果上一个任务没有执行完毕,则需要等上一个任务执行完毕后立即执行。FixedRate: 固定比率 本test现象:执行完,没有歇息,直接开始下一次。

这个是尽力去实现那个间隔,除非run很耽误时间,run的时间超过了设置的执行间隔,才会延迟,with的,即使run时间短,他也是在一点点的延迟的,这个就不会。 

源码地址:https://github.com/cmshome/JavaNote/blob/master/tool/src/test/java/com/lxk/tool/monitor/TestMonitor.java

以上是关于Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别的主要内容,如果未能解决你的问题,请参考以下文章

Java定时线程实现:scheduleAtFixedRate 和 scheduleWithFixedDelay 的差别

java多线程实现定时器任务

java定时任务使用多线程webservcie执行了两次这是为啥?

定时任务多线程的实现

VC 怎样用线程来实现定时器?

SpringBoot几种定时任务的实现方式 和多线程执行任务