scheduleAtFixedRate与scheduleWithFixedDelay


117

ScheduledExecutorServicescheduleAtFixedRatescheduleWithFixedDelay方法之间的主要区别是什么?

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);

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

Answers:


206

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

例如,假设我安排一个警报以固定的速率每小时发出一次,并且每次发出警报,我都会喝一杯咖啡,这需要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

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


17
如果需要花费一个多小时来煮咖啡,那么在fixedRate方案中会发生什么?
Brett VanderVeen 2014年

5
@BrettVanderVeen:我相信这取决于有问题的执行人。它将按时间计划 -但是是否执行取决于线程是否对该执行器可用。我建议您尝试一下,看看它在各种情况下如何工作。
乔恩·斯基特

8
@BrettVanderVeen从文档中,“如果此任务的任何执行花费的时间超过其周期,那么后续执行可能会延迟启动,但不会同时执行。” 换句话说,一致的实现将不允许下一个执行,直到前一个完成为止。
M. Justin'Mar

您能为像我这样的新手提供所显示输出(咖啡)的工作代码吗?
MuneshSingh's

@MuneshSingh:不是这个问题,它是要解释以固定速率进行调度和以固定延迟进行调度之间的区别。无论如何,您都不会自己实现它-您将使用内置的执行程序。
乔恩·斯基特

57

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

调用scheduleTimeAtFixedRate方法的时间序列

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

ScheduleWithFixedDelay方法的时间序列

希望可以帮到你


我无法理解scheduleAtFixedRate时间序列图中提到的“额外”一词。
MuneshSingh's

1
@MuneshSingh旨在表明任务的执行时间比计划的要长,因此需要“额外的”时间,下一次执行立即开始。
Viorel

@Viorel感谢您的澄清。这是否意味着“周期”并不是两个连续执行之间的固定时间延迟。
MuneshSingh

1
@MuneshSingh周期是固定的,但是一旦通过当前的任务就不会停止,在此运行与下一个运行之间不会有任何延迟。如果要创建“超时”,则可能要保留Future并在其他执行程序中将其取消。用简单的话说,它是在“时间”过后尽快开始执行下一个执行
Viorel

4

scheduleAtFixedRate()方法将创建一个新任务,并每隔一段时间将其提交给执行者,而不管先前的任务是否完成

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


您写了两次scheduleAtFixedRate:)
Vlad19年

3

如果您阅读Java Doc,它将更加清晰

ScheduledFuture scheduleAtFixedRate(Runnable命令,long initialDelay,长周期,TimeUnit单位) 创建并执行一个周期性操作,该操作在给定的初始延迟后首先启用,然后在给定的周期后启用;也就是说执行将在initialDelay,initialDelay + period,initialDelay + 2 * period等之后开始。

ScheduledFuture scheduleWithFixedDelay(Runnable命令,long initialDelay,长延迟,TimeUnit单位) 创建并执行一个周期性操作,该操作将在给定的初始延迟后首先启用,然后以给定的延迟在一次执行终止与下一次执行之间延迟。


1

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

我认为这将帮助您选择方法因为这个我遇到了很大的问题


1
什么?JVM会决定吗?那到底是什么意思?确实,可运行对象不会按照文档并发执行,但这是由执行者决定的,它可以是自定义的,也可以是标准的ScheduledThreadPoolExecutor(标准具有明确的行为)
Ordous 2014年

不,我在我的应用程序中发现了类似的问题,其中我给了15分钟的间隔,并且第一个任务在15分钟内没有完成,并且花费了15.30秒,所以第二个任务没有立即启动,因此它在5分钟后和一段时间后才开始启动8分钟,我不知道我们是否可以控制这种行为,因为这不是标准行为。
user1047873 2014年

这听起来像教科书任务排队。
2014年

是的,这仅意味着执行程序中的所有线程已经在忙于执行某项操作,并且您的任务被排入待执行的事情队列。(注意,您需要通过查看所述队列或查看执行程序线程在做什么来确认这一点)。您如何控制它取决于您拥有哪种类型的执行器。您可能只想为该特定任务创建一个单独的1线程执行程序,这样它就不会等待任何事情。或者给您当前的执行程序更多线程。或更改其策略。
2014年

0

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

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消除延迟


-1
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);

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


1
还请说明代码如何解决OP的问题。:)
Yash
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.