scheduleAtFixedRate 与 scheduleWithFixedDelay

Posted

技术标签:

【中文标题】scheduleAtFixedRate 与 scheduleWithFixedDelay【英文标题】:scheduleAtFixedRate vs scheduleWithFixedDelay 【发布时间】:2014-08-30 06:03:22 【问题描述】:

scheduleAtFixedRate和ScheduledExecutorService的scheduleWithFixedDelay方法的主要区别是什么?

scheduler.scheduleAtFixedRate(new Runnable() 
    @Override
    public void run() 
        System.out.println("scheduleAtFixedRate:    " + new Date());
    
, 1, 3L , SECONDS);

scheduler.scheduleWithFixedDelay(new Runnable() 
    @Override
    public void run() 
        System.out.println("scheduleWithFixedDelay: " + new Date());
    
, 1, 3L , SECONDS);

它们在完全相同的时间打印,似乎它们以完全相同的间隔执行。

【问题讨论】:

【参考方案1】:

尝试在您的run() 方法中添加一个Thread.sleep(1000); 调用...基本上这是根据上一次执行的时间结束和它(逻辑上)开始时间安排某事之间的区别.

例如,假设我安排一个闹钟以每小时一次的固定频率响起,每次响起时,我都会喝一杯咖啡,这需要 10 分钟。假设从午夜开始,我会:

00:00: Start making coffee
00:10: Finish making coffee
01:00: Start making coffee
01:10: Finish making coffee
02:00: Start making coffee
02:10: Finish making coffee

如果我安排一个固定的延迟一小时,我会:

00:00: Start making coffee
00:10: Finish making coffee
01:10: Start making coffee
01:20: Finish making coffee
02:20: Start making coffee
02:30: Finish making coffee

你想要哪一个取决于你的任务。

【讨论】:

如果煮咖啡的时间超过一个小时,在 fixedRate 场景中会发生什么? @BrettVanderVeen:我相信这取决于相关的执行人。它将按时调度 - 但是否执行 取决于该执行程序是否有线程可用。我建议您尝试一下,看看它在各种情况下是如何工作的。 @BrettVanderVeen 来自the documentation,“如果此任务的任何执行时间超过其周期,则后续执行可能会延迟开始,但不会同时执行。”换句话说,一个符合要求的实现在前一个完成之前不允许下一个执行。 您能否为像我这样的新手提供显示的输出(咖啡)的工作代码? @MuneshSingh:不在这个问题中,它要求解释以固定速率调度和以固定延迟调度之间的区别。无论如何,你不会自己实现这个 - 你会使用内置的执行器。【参考方案2】:

可视化调用scheduleAtFixedRate方法的时间序列。如果最后一个执行时间超过 period,则下一个执行将立即开始。否则,它将在一段时间后开始。

调用scheduleWithFixedDelay方法的时间序列。下一个执行将在一个执行终止和下一个执行开始之间的延迟时间之后开始,无论其执行时间如何

希望能帮到你

【讨论】:

我无法理解 scheduleAtFixedRate 时间序列图中提到的“额外”一词。 @MuneshSingh 表示任务的执行时间比预定时间长,所以需要“额外”的时间,下一次执行马上开始。 @Viorel 感谢您的澄清。这是否意味着“周期”并不完全是两次连续执行之间的固定时间延迟。 @MuneshSingh 周期是固定的,但是一旦通过它就不会停止当前的任务,在这个运行和下一个运行之间根本没有延迟。如果您想创建“超时”,您可能希望保留 Future 并在不同的执行程序中取消它。简而言之,它表示在“周期”时间过去后尽快开始第一次执行和下一次执行【参考方案3】:

scheduleAtFixedRate() 方法创建一个新任务,并在每个周期将其提交给执行器,无论之前的任务是否完成

另一方面,scheduleWithFixedDelay() 方法在前一个任务完成后创建一个新任务。

【讨论】:

你写了两次scheduleAtFixedRate :)【参考方案4】:

如果你阅读 Java Doc 会更清楚

ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) 创建并执行一个周期性动作,该动作在给定的初始延迟后首先启用,随后在给定的周期内启用;也就是说,执行将在 initialDelay 之后开始,然后是 initialDelay+period,然后是 initialDelay + 2 * period,依此类推。

ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) 创建并执行一个周期性操作,该操作首先在给定的初始延迟之后启用,随后在一个执行的终止和下一个执行的开始之间具有给定的延迟。

【讨论】:

【参考方案5】:

如果第一个线程花费太长时间并且没有在给定的持续时间内结束,则 scheduleAtFixedRate 中有一个问题,那么一旦第一个任务完成,第二个连续线程将不会启动,并且不会在第一个线程完成任务时立即启动并且给定的持续时间已经过去。 JVM 将决定何时执行下一个任务。

我认为这会帮助你选择方法因为我遇到了大问题

【讨论】:

什么? JVM会决定吗?这到底是什么意思?确实,runnable 不会根据文档与其自身同时执行,但它由执行者决定,这可能是自定义的或标准的 ScheduledThreadPoolExecutor(后者具有明确定义的行为) 不,我在我的应用程序中发现了类似的问题,我给了 15 分钟的间隔,第一个任务没有在 15 分钟内完成并且需要 15.30 秒,所以第二个任务没有立即开始它有时5 分钟后开始,8 分钟后开始,我不知道我们是否可以控制这种行为,因为这不是标准行为。 这听起来像教科书的任务排队。 是的,这只是意味着你的执行程序中的所有线程都已经在忙着做某事,而你的任务被放入一个待办事项队列中。 (注意您需要通过查看所述队列或查看执行程序线程正在做什么来确认这一点)。你如何控制这取决于你有什么类型的执行者。您可能只想为这个特定任务创建一个单独的 1 线程执行器,然后它不会等待任何东西。或者给你当前的执行者更多的线程。或者改变它的策略。【参考方案6】:

让我们写一个简单的程序:

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

var time = 0L
var start = System.currentTimeMillis()
val executor = Executors.newScheduledThreadPool(1)
executor.scheduleWithFixedDelay(
    if (time >= 12_000L) 
        executor.shutdown()
     else 
        Thread.sleep(2000L)
        val now = System.currentTimeMillis()
        time += now - start
        System.out.println("Total $time delay $now - start\n")
        start = now
    
, 0L, 1000L, TimeUnit.MILLISECONDS)

并查看结果:

| scheduleWithFixedDelay |   scheduleAtFixedRate  |
|:----------------------:|:----------------------:|
| Total 2001 delay 2001  | Total 2003 delay 2003  |
| Total 5002 delay 3001  | Total 4004 delay 2001  |
| Total 8003 delay 3001  | Total 6004 delay 2000  |
| Total 11003 delay 3000 | Total 8004 delay 2000  |
| Total 14003 delay 3000 | Total 10005 delay 2001 |
|          ---           | Total 12005 delay 2000 |

注意执行时间大于等待时间

scheduleWithFixedDelay 保持延迟scheduleAtFixedRate 消除延迟

【讨论】:

【参考方案7】:

我可以看到你的前提,但你的结论并不完全正确。 根据this Tutorial,这是一个很好且非常完整的解释,用于理解这两者之间的差异。

scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

此方法安排任务定期执行。该任务在initialDelay之后第一次执行,然后每次周期到期时重复执行。 如果给定任务的任何执行引发异常,则不再执行该任务。如果没有抛出异常,任务将继续执行,直到 ScheduledExecutorService 关闭。 如果一个任务的执行时间比其计划执行之间的时间长,则下一次执行将在当前执行完成后开始。计划任务一次不会被多个线程执行。

scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

此方法的工作方式与 scheduleAtFixedRate() 非常相似,只是对周期的解释不同。 在 scheduleAtFixedRate() 方法中,周期被解释为上一次执行开始到下一次执行开始之间的延迟。 但是,在此方法中,周期被解释为上一次执行结束到下一次执行开始之间的延迟。因此,延迟是在完成的执行之间,而不是在执行开始之间。

【讨论】:

【参考方案8】:
scheduledExecutorService.scheduleAtFixedRate(() -> 
        System.out.println("runnable start"); try  Thread.sleep(5000);  System.out.println("runnable end"); catch
     (InterruptedException e)  // TODO Auto-generated catch block
      e.printStackTrace(); , 2, 7, TimeUnit.SECONDS);



     scheduledExecutorService.scheduleWithFixedDelay(() -> 
     System.out.println("runnable start"); try  Thread.sleep(5000); System.out.println("runnable end"); catch
     (InterruptedException e)  // TODO Auto-generated catch block
     e.printStackTrace();  , 2, 7, TimeUnit.SECONDS);

只需执行它,您就会知道其中的区别。谢谢

【讨论】:

还请解释代码如何解决 OP 的问题。 :)

以上是关于scheduleAtFixedRate 与 scheduleWithFixedDelay的主要内容,如果未能解决你的问题,请参考以下文章

scheduleAtFixedRate 与 scheduleWithFixedDelay

scheduleAtFixedRate 与 scheduleWithFixedDelay 的区别

scheduleAtFixedRate与scheduleWithFixedDelay区别

scheduleAtFixedRate与scheduleWithFixedDelay有啥不同

定时任务调度工作(学习记录 四)schedule与scheduleAtFixedRate的区别

schedule和scheduleAtFixedRate的区别