调度固定速率与调度固定延迟

计划执行服务scheduleAtFixedRatescheduleWithFixedDelay方法的主要区别是什么?

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

它们打印的时间完全相同,看起来它们是在完全相同的时间间隔内执行的。

87412 次浏览

尝试在您的 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

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

如果你阅读 Java 文档,它会更加清晰

未来调度 AtFixedRate (可运行命令,长初始延迟,长周期,TimeUnit 单元) 创建并执行一个周期性操作,该操作在给定的初始延迟之后首先启用,然后在给定的时间段之后启用; 也就是说,执行将在初始延迟之后开始,然后是初始延迟 + 时间段,然后是初始延迟 + 2 * 时间段,以此类推。

未来调度与固定延迟(可运行命令,长初始延迟,长延迟,时间单位单位) 创建并执行一个周期性操作,该操作在给定的初始延迟之后首先启用,然后在一次执行终止和下一次执行开始之间的给定延迟中启用。

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

我认为这将有助于你选择方法,因为由于这个我有大问题

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

time series of invocation scheduleAtFixedRate method

调用 scheduleWithFixedDelay方法的时间序列。下一次执行将在从一次执行终止到下一次执行开始之间的延迟时间之后开始,而与其执行时间无关

time series of invocation scheduleWithFixedDelay method

Hope 可以帮你

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

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

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

只要执行它,你就会知道区别。谢谢

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

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 |

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

ScheduleWithFixed延迟 保持延迟
ScheduleAtFixedRate 消除延迟

我能看出你的前提,但是你的结论并不完全正确。 这里是一个很好的,相当完整的解释,根据 本教程了解这两者之间的差异。

ScheduleAtFixedRate (可运行、长初始延迟、长周期、 TimeUnit 时间单元)

此方法调度要定期执行的任务。该任务首次执行后,初始延迟,然后每次周期过期重复执行。 如果任何给定任务的执行引发异常,则不再执行该任务。如果没有引发异常,任务将继续执行,直到 ScheduledExecutorService 关闭。 如果一个任务的执行时间长于其计划执行之间的时间间隔,那么下一次执行将在当前执行结束后开始。计划的任务不会一次由多个线程执行。

可运行、长初始延迟、长周期、 TimeUnit 时间单元

除了对周期的解释不同之外,这个方法的工作原理非常类似于 ScheduleAtFixedRate ()。 在 ScheduleAtFixedRate ()方法中,周期被解释为从上一次执行开始到下一次执行开始之间的延迟。 但是,在这个方法中,周期被解释为从前一次执行结束到下一次执行开始之间的延迟。因此,延迟是在完成执行之间,而不是在开始执行之间。