• scheduleAtFixedRate和scheduleWithFixedDelay探究


    scheduleAtFixedRate是用任务开始时间计算间隔,就是说某任务上次理论启动时间+间隔时间就是下次启动时间。
    scheduleWithFixedDelay是用任务结束时间计算间隔,就是说某任务上次结束时间+间隔时间就是下次启动时间。

    这段代码模拟了一组10个任务,每个任务都有个name(任务名)和time(任务花费的时间)。第6个任务(任务名为任务5)耗时15s,其他任务耗时1s。这一组任务分别用scheduleAtFixedRate(固定频率)和scheduleWithFixedDelay(固定延迟)处理,截取部分日志解析这两个方法的运行逻辑。

    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.concurrent.*;
    
    @Slf4j
    public class MyConcurrentTest {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            ScheduledExecutorService readThreadPool = Executors.newScheduledThreadPool(5);
            List<ScheduledFuture> futureList = new ArrayList<>();
            //用objects模拟任务列表,任务的time代表耗费的时间
            List<Map<String,Object>> objects = new ArrayList<>();
            for(int i = 0 ; i < 10 ; i++){
                Map<String,Object> object = new HashMap<>();
                object.put("name","任务"+i);
                object.put("time",1000);
                objects.add(object);
            }
            //第6个任务sleep15s,第6个任务是任务5
            objects.get(5).put("time",15000);
            System.out.println(objects);
            for(Map<String,Object> object : objects){
                //任务在0s后开始,任务间隔是5s
                ScheduledFuture future = readThreadPool.scheduleAtFixedRate(()->{
                    log.info(object.toString());
                    try {
                        //暂停指定时间
                        Thread.sleep((int)object.get("time"));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                },0,5, TimeUnit.SECONDS);
                futureList.add(future);
            }
            for(ScheduledFuture future:futureList){
                future.get();
            }
        }
    }
    

    用scheduleAtFixedRate(固定频率)处理的日志。

    --10个任务的详情
    [{name=任务0, time=1000}, {name=任务1, time=1000}, {name=任务2, time=1000}, {name=任务3, time=1000}, {name=任务4, time=1000}, {name=任务5, time=15000}, {name=任务6, time=1000}, {name=任务7, time=1000}, {name=任务8, time=1000}, {name=任务9, time=1000}]
    --线程池有5个核心线程,同时开始了5个任务。
    11:06:06.505 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:06:06.505 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:06:06.505 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:06:06.505 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    11:06:06.505 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务2, time=1000}
    --1s之后任务处理完,处理其他的任务。
    11:06:07.505 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务5, time=15000}
    11:06:07.505 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务9, time=1000}
    11:06:07.505 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:06:07.505 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:06:07.505 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务8, time=1000}
    --4s之后距离任务0/1/2/3/4上次启动已经过去了5s,任务0/1/2/3/4都已经准备好第二次执行,但此时只有四个空闲线程。按照顺序任务4没有执行。
    11:06:11.505 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:06:11.505 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:06:11.505 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:06:11.505 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务3, time=1000}
    --1s之后除了任务4,任务5/6/7/8/9也都准备好第二次执行,但此时任务5第一次执行还没有结束,所以任务5第二次执行被阻塞。按照顺序,任务9没有执行。
    11:06:12.506 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:06:12.506 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务8, time=1000}
    11:06:12.506 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:06:12.506 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务7, time=1000}
    --1s之后任务5/9准备好执行,此时任务5第二次执行依然被阻塞。
    11:06:13.521 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务9, time=1000}
    --3s之后任务0/1/2/3准备好第三次执行……
    11:06:16.506 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:06:16.506 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:06:16.506 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:06:16.506 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    --1s之后……
    11:06:17.506 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:06:17.506 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:06:17.506 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务8, time=1000}
    11:06:17.506 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务6, time=1000}
    --1s之后……
    11:06:18.506 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务9, time=1000}
    --3s之后……
    11:06:21.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:06:21.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    11:06:21.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:06:21.507 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务2, time=1000}
    --1s之后,任务5第二次执行,因为任务5第一次执行开始时间到现在时间间隔大于规定的时间间隔,即任务5第二次执行已经“迟到”,应立即执行。
    11:06:22.507 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务5, time=15000}
    11:06:22.522 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:06:22.522 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务8, time=1000}
    11:06:22.522 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:06:22.522 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:06:23.523 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务9, time=1000}
    

    综上所述,scheduleAtFixedRate(固定频率)是在任务上次开始时间+间隔时间开始的,拿任务0来说,第一次启动是在11:06:06,第二次启动是在11:06:11,中间间隔了5s,这正是我们规定的间隔时间,以此来达到频率固定的效果(当然频率也不是绝对固定的,如任务4)。

    java.util.concurrent.ScheduledThreadPoolExecutor.ScheduledFutureTask#run源码得知,只有在任务从队列中取出并执行时才会往任务队列中放入下次任务。下次任务的启动时间是根据本次任务的理论启动时间计算的(任务队列中的每个任务都会有一个理论启动时间,但是可能会因为没有抢到线程而延迟,所以任务的实际启动时间不一定是理论启动时间)。所以,如果任务A执行时间过长,任务A'会一直等待(即使超时),当任务A'执行的时候可能会把任务A''的启动时间设置到任务A'实际启动时间之前。

    /**
        * Overrides FutureTask version so as to reset/requeue if periodic.
        */
    public void run() {
        boolean periodic = isPeriodic();
        if (!canRunInCurrentRunState(periodic))
            cancel(false);
        else if (!periodic)
            ScheduledFutureTask.super.run();
        else if (ScheduledFutureTask.super.runAndReset()) {
            /*在任务执行时,修改任务下次启动时间并重新放回队列。
             *scheduleAtFixedRate就会以此启动时间+间隔时间计算下次启动时间。
             */
            setNextRunTime();
            reExecutePeriodic(outerTask);
        }
    }
    

    注意:如果任务都是第一次处理很耗时间,之后耗时很短的话,会出现连续处理多次某个任务的情况。上面的代码,我们在lambda表达式最后一行添加object.put("time",0);,在处理完第一遍之后把所有任务的处理时间全都设成0s。这样当任务5执行完之后,任务5会立刻执行多次。如下日志:

    12:48:14.490 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务4, time=1000}
    12:48:14.490 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    12:48:14.490 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务2, time=1000}
    12:48:14.490 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务1, time=1000}
    12:48:14.490 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务3, time=1000}
    12:48:15.490 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务7, time=1000}
    12:48:15.490 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务9, time=1000}
    12:48:15.490 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务6, time=1000}
    12:48:15.490 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务8, time=1000}
    12:48:15.490 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务5, time=15000}
    12:48:19.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务0, time=0}
    12:48:19.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务1, time=0}
    12:48:19.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务2, time=0}
    12:48:19.507 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务3, time=0}
    12:48:19.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务4, time=0}
    12:48:19.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务6, time=0}
    12:48:19.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务7, time=0}
    12:48:19.507 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务8, time=0}
    12:48:19.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务9, time=0}
    12:48:24.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务1, time=0}
    12:48:24.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=0}
    12:48:24.507 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务2, time=0}
    12:48:24.507 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务3, time=0}
    12:48:24.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务4, time=0}
    12:48:24.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务6, time=0}
    12:48:24.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务7, time=0}
    12:48:24.507 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务8, time=0}
    12:48:24.507 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务9, time=0}
    12:48:29.509 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务1, time=0}
    12:48:29.509 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务2, time=0}
    12:48:29.509 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务3, time=0}
    12:48:29.509 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务0, time=0}
    12:48:29.509 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务4, time=0}
    12:48:29.509 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务6, time=0}
    12:48:29.509 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务7, time=0}
    12:48:29.509 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务8, time=0}
    12:48:29.509 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务9, time=0}
    --连续执行了三遍任务5
    12:48:30.493 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务5, time=0}
    12:48:30.493 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务5, time=0}
    12:48:30.493 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务5, time=0}
    

    用scheduleWithFixedDelay(固定延迟)处理的日志。

    --10个任务的详情
    [{name=任务0, time=1000}, {name=任务1, time=1000}, {name=任务2, time=1000}, {name=任务3, time=1000}, {name=任务4, time=1000}, {name=任务5, time=15000}, {name=任务6, time=1000}, {name=任务7, time=1000}, {name=任务8, time=1000}, {name=任务9, time=1000}]
    --线程池有5个核心线程,同时开始了5个任务。
    11:51:51.450 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:51:51.450 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:51:51.450 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:51:51.450 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:51:51.450 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    --1s之后任务处理完,处理其他的任务。
    11:51:52.466 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:51:52.466 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务9, time=1000}
    11:51:52.466 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:51:52.466 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务8, time=1000}
    11:51:52.466 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务5, time=15000}
    --5s之后,距离任务0/1/2/3/4上次处理结束已经过去了5s,任务0/1/2/3/4都已经准备好第二次执行,但此时只有四个空闲线程。按照顺序任务4没有执行。
    11:51:57.467 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:51:57.467 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:51:57.467 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:51:57.467 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务3, time=1000}
    --1s之后除了任务4,任务6/7/8/9也都准备好第二次执行,任务5还没有结束第一次执行。
    11:51:58.467 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务9, time=1000}
    11:51:58.467 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:51:58.467 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:51:58.467 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务7, time=1000}
    --1s之后执行任务8。
    11:51:59.483 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务8, time=1000}
    --4s之后任务0/1/2/3准备好第三次执行……
    11:52:03.468 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    11:52:03.468 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:52:03.468 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:52:03.468 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    --1s之后……
    11:52:04.484 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:52:04.484 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:52:04.484 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:52:04.484 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务9, time=1000}
    --1s之后……
    11:52:05.500 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务8, time=1000}
    --4s之后……
    11:52:09.469 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:52:09.469 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:52:09.469 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务3, time=1000}
    11:52:09.469 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务2, time=1000}
    --1s之后……
    11:52:10.501 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:52:10.501 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:52:10.501 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:52:10.501 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务9, time=1000}
    --1s之后……
    11:52:11.501 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务8, time=1000}
    --1s之后距离任务5处理(11:51:52--11:52:07)结束已经过去了5s,任务5第二次执行。
    11:52:12.471 [pool-1-thread-1] INFO MyConcurrentTest - {name=任务5, time=15000}
    11:52:15.471 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务1, time=1000}
    11:52:15.471 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务0, time=1000}
    11:52:15.471 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务3, time=1000}
    11:52:15.471 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务2, time=1000}
    11:52:16.518 [pool-1-thread-4] INFO MyConcurrentTest - {name=任务4, time=1000}
    11:52:16.518 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务7, time=1000}
    11:52:16.518 [pool-1-thread-2] INFO MyConcurrentTest - {name=任务9, time=1000}
    11:52:16.518 [pool-1-thread-3] INFO MyConcurrentTest - {name=任务6, time=1000}
    11:52:17.534 [pool-1-thread-5] INFO MyConcurrentTest - {name=任务8, time=1000}
    

    综上所述,scheduleWithFixedDelay(固定延迟)是在任务上次结束时间+间隔时间开始的,任务如果没有结束,任务队列是没有下次执行计划的。

  • 相关阅读:
    C# 深浅复制 MemberwiseClone
    负载均衡算法,轮询方式
    大话设计模式之工厂模式 C#
    大话设计模式:代理模式 C#
    C# 单元测试
    【前端安全】JavaScript防http劫持与XSS
    神秘的 shadow-dom 浅析
    【CSS进阶】伪元素的妙用2
    【CSS进阶】CSS 颜色体系详解
    【CSS进阶】box-shadow 与 filter:drop-shadow 详解及奇技淫巧
  • 原文地址:https://www.cnblogs.com/macho8080/p/12264128.html
Copyright © 2020-2023  润新知