我正在使用spring实现预定任务,我看到有2个类型的配置时间,从最近再次安排工作。 2配置这个配置有什么不同。
@Scheduled(fixedDelay = 5000)
public void doJobDelay() {
// do anything
}
@Scheduled(fixedRate = 5000)
public void doJobRate() {
// do anything
}
在代码中:
@Scheduled(fixedDelay=5000)
public void updateEmployeeInventory(){
System.out.println("employee inventory will be updated once only the last updated finished ");
/**
* add your scheduled job logic here
*/
}
@Scheduled(fixedRate=5000)
public void updateEmployeeInventory(){
System.out.println("employee inventory will be updated every 5 seconds from prior updated has stared, regardless it is finished or not");
/**
* add your scheduled job logic here
*/
}
“fixedRate”:在开始下一次执行之前等待上一次执行开始时的X millis。如果当前执行超过'fixedRate'间隔,则下一次执行将排队,但只有下一次执行。它不会创建一系列排队执行
private static int i = 0;
@Scheduled(initialDelay=1000, fixedRate=1000)
public void testScheduling() throws InterruptedException {
System.out.println("Started : "+ ++i);
Thread.sleep(4000);
System.out.println("Finished : "+ i);
}
输出:
开始:1 完成:1 // 4秒后 开始:2 //立即按固定费率指定等待1秒 完成:2 // 4秒后 等等
“fixedDelay”:在开始下一次执行之前等待上一次执行结束时的X millis。无论当前执行花费多少时间,在将“fixedDelay”间隔添加到当前执行结束时间之后开始下一次执行。它不会排队下次执行。
private static int i = 0;
@Scheduled(initialDelay=1000, fixedDelay=1000)
public void testScheduling() throws InterruptedException {
System.out.println("Started : "+ ++i);
Thread.sleep(4000);
System.out.println("Finished : "+ i);
}
输出:
开始:1 完成:1 // 4秒后启动:2 //按照fixedDelay中的指定等待1秒完成:2 // 4秒后启动:3 // 1秒后 等等
fixedRate:用于每n毫秒运行一次作业方法。这项工作是否已完成上一项任务并不重要。
fixedDelay:用于按任务之间给定的n毫秒等待时间顺序运行作业方法。
何时使用“fixedRate”:如果预计不超过内存和线程池的大小,则fixedRate是合适的。如果传入的任务没有快速完成,最终可能会出现“Out of Memory exception”
何时使用“fixedDelay”:如果每个正在运行的任务彼此相关并且需要在前一个任务完成之前等待,则fixedDelay是合适的。如果小心设置fixedDelay时间,它还将让正在运行的线程有足够的时间在新任务开始之前完成其作业
应该澄清的一点是,fixedRate
并不意味着执行将以一定的时间间隔开始。
如果一次执行花费太多时间(超过固定费率),则下一次执行将仅在前一次执行完成后开始,除非提供@Async
和@EnableAsync
。以下源代码是Spring的ThreadPoolTaskScheduler
实现的一部分,解释了原因:
@Override
public void run() {
Date actualExecutionTime = new Date();
super.run();
Date completionTime = new Date();
synchronized (this.triggerContextMonitor) {
this.triggerContext.update(this.scheduledExecutionTime, actualExecutionTime, completionTime);
if (!this.currentFuture.isCancelled()) {
schedule();
}
}
}
您可以看到只有在上一个任务完成后(super.run()
),才会安排下一个任务(schedule()
)。使用@Async
和@EnableAsync
,super.run()
是一个异步函数,它将立即返回,因此下一个任务不必等待前一个实际完成。
我们可以使用Spring的@Scheduled
注释运行计划任务,但基于属性fixedDelay
和fixedRate
执行的性质会发生变化。
fixedDelay
属性确保在执行任务的n
和下一次执行任务的millisecond
之间存在finish time
start time
的延迟。
当我们需要确保只有一个任务实例一直运行时,此属性特别有用。对于从属工作,这是非常有帮助的。
fixedRate
财产在每个n
millisecond
运行预定的任务。它不检查任何先前执行的任务。
当任务的所有执行都是独立的时,这很有用。如果我们不希望超过内存和线程池的大小,fixedRate
应该非常方便。
但是,如果传入的任务没有快速完成,则可能最终会出现“Out of Memory exception”。
固定延迟:专门控制上次执行完成时的下一个执行时间。
固定速率:即使最后一次调用仍在运行,Spring也会定期运行任务。
关于这些方法的作用,似乎存在相互矛盾的建议。也许行为可能会改变,具体取决于使用spring上下文注册的taskScheduler
或Executor
bean。我发现@Ammar Akouri的回答是最接近的。
这是我在使用ScheduledThreadPoolExecutor
时发现的(下面提供的完整测试源)
fixedDelay
和fixedRate
都不允许同时执行任务fixedDelay
将等待上一次调用的结束,然后在将来的固定时间内安排新的inovcation。因此,它不会一次排队多个任务。fixedRate
将在每个时期安排一次新的调用。它将一次排队多个任务(可能是无限制的),但永远不会同时执行任务。样品测试(Kotlin / JUnit):
class LearningSchedulerTest {
private lateinit var pool: ScheduledExecutorService
@Before
fun before() {
pool = Executors.newScheduledThreadPool(2)
}
@After
fun after() {
pool.shutdown()
}
/**
* See: https://stackoverflow.com/questions/24033208/how-to-prevent-overlapping-schedules-in-spring
*
* The documentation claims: If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.
* https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#scheduleAtFixedRate-java.lang.Runnable-long-long-java.util.concurrent.TimeUnit-
*/
@Test
fun `scheduleAtFixedRate schedules at fixed rate`() {
val task = TaskFixture( initialSleep = 0)
pool.scheduleAtFixedRate({task.run()}, 0, 10, TimeUnit.MILLISECONDS )
Thread.sleep(15)
Assert.assertEquals(2, task.invocations.get())
Thread.sleep(10)
Assert.assertEquals(3, task.invocations.get())
Thread.sleep(10)
// 1 initial and 3 periodic invocations
Assert.assertEquals(4, task.invocations.get())
}
@Test
fun `scheduleAtFixedRate catches up on late invocations`() {
val task = TaskFixture(initialSleep = 30)
pool.scheduleAtFixedRate({task.run()}, 0, 10, TimeUnit.MILLISECONDS )
Thread.sleep(15) // we see no concurrent invocations
Assert.assertEquals(1, task.invocations.get())
Thread.sleep(10) // still no concurrent invocations
Assert.assertEquals(1, task.invocations.get())
Thread.sleep(10)
// 1 initial and 3 periodic invocations
Assert.assertEquals(4, task.invocations.get())
}
@Test
fun `scheduleWithFixedDelay schedules periodically`() {
val task = TaskFixture( initialSleep = 0)
pool.scheduleWithFixedDelay({task.run()}, 0, 10, TimeUnit.MILLISECONDS )
Thread.sleep(35)
// 1 initial and 3 periodic invocations
Assert.assertEquals(4, task.invocations.get())
}
@Test
fun `scheduleWithFixedDelay does not catch up on late invocations`() {
val task = TaskFixture( initialSleep = 30)
pool.scheduleWithFixedDelay({task.run()}, 0, 10, TimeUnit.MILLISECONDS )
Thread.sleep(35)
// 1 initial invocation, no time to wait the specified 10ms for a second invocation
Assert.assertEquals(1, task.invocations.get())
}
class TaskFixture(val initialSleep: Long) {
var invocations = AtomicInteger()
fun run() {
invocations.incrementAndGet()
if (invocations.get() == 1){
Thread.sleep(initialSleep)
}
}
}
}