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)。如果任务执行时间过长,任务依然会添加到任务队列,不过会阻塞,等任务执行完之后才会再次执行。

注意:如果任务都是第一次处理很耗时间,之后耗时很短的话,会出现连续处理多次某个任务的情况。上面的代码,我们在lambda表达式最后一行添加object.put("time",0);,在处理往第一遍之后把所有的任务的处理时间全都设成0s,这样当任务5执行完之后,任务队列里就会有3个耗时为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(固定延迟)是在任务上次结束时间+间隔时间开始的,任务如果没有结束,任务队列是没有下次执行计划的。

原文地址:https://www.cnblogs.com/macho8080/p/12264128.html

时间: 2024-10-29 16:14:54

scheduleAtFixedRate和scheduleWithFixedDelay探究的相关文章

ScheduledThreadPoolExecutor线程池scheduleAtFixedRate和scheduleWithFixedDelay的区别

ScheduledFuture<?> result = executor.scheduleAtFixedRate(task,2, 5, TimeUnit.SECONDS); 在延迟2秒之后开始执行首个任务,之后每隔5秒执行一个任务,也就是固定间隔时间执行一次任务,而不是等到上个任务执行结束. ScheduledFuture<?> result = executor.scheduleWithFixedDelay(task,2, 5, TimeUnit.SECONDS); 在延迟2秒后

Java线程池应用

Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThreads 线程会处于处理任务的活动状态.如果在所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待.如果在关闭前的执行期间由于失败而导致任何线程终止,那么一个新线程将代替它执行后续的任务(如果需要).在某个线程被显式地关闭之前,池中的线程将一直存在. 创建一个固定大小的线程池来执

几种任务调度的 Java 实现方法与比较

几种任务调度的 Java 实现方法与比较 综观目前的 Web 应用,多数应用都具备任务调度的功能.本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给需要开发任务调度的程序员提供有价值的参考. 前言 任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务.本文由浅入深介绍四种任务调度的 Java 实现: Timer ScheduledExecutor 开源工具包 Qua

Java并发编程——Executor接口及线程池的使用

在如今的程序里,单线程的程序,应该已经比较少了,而Java语言是内置支持多线程并发的,大家都说Java语言内置支持多线程,非常非常的强大和方便,但一直没有深入研究jdk内concurrent包.今天就认真学习了一下java.util.concurrent包,发现jdk多线程编程果然是强大和方便.本文是学习java.util.concurrent包内线程池及相关接口的一些总结. 任务接口抽象 Runnable接口 在java.lang包内,为多线程提供了Runnable接口. public int

Java线程同步与死锁、生产者消费者模式以及任务调度等

一.Thread类基本信息方法 package Threadinfo; public class MyThread implements Runnable{ private boolean flag = true; private int num = 0; @Override public void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"-->"+num++); } }

ScheduledThreadPoolExecutor源码主要部分解析

ScheduledThreadPoolExecutor继承与基础线程池类ThreadPoolExecutor并实现ScheduledExecutorService接口. 其中ScheduledExecutorService继承与ExecutorService接口并添加了scheduleAtFixedRate和scheduleWithFixedDelay等方法. 两个方法的区别是前者是周期性的按照一定的时间进行任务的执行.如果一个任务执行超过了周期时间,则任务执行完之后会马上进行下一次任务的执行.

Java任务调度

1.Timer package com.qhong; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class Main { public static void main(String[] args) { Timer timer = new Timer(); long delay1 = 1 * 1000; l

几种任务调度的Java实现方法与比较

简介: 综观目前的 Web 应用,多数应用都具备任务调度的功能.本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给需要开发任务调度的程序员提供有价值的参考. 任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务.本文由浅入深介绍四种任务调度的 Java 实现: Timer ScheduledExecutor 开源工具包 Quartz 开源工具包 JCronTab 此

java的concurrent用法详解

我们都知道,在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便.而当针对高质量Java多线程并发程序设计时,为防止死蹦等现象的出现,比如使用java之前的wait().notify()和synchronized等,每每需要考虑性能.死锁.公平性.资源管理以及如何避免线程安全性方面带来的危害等诸多因素,往往会采用一些较为复杂的安全策略,加重了程序员的开发负担.万幸的是,在JDK1.5出