@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
*/
}
如果一次执行花费了太多的时间(超过固定的速率) ,那么下一次执行将只启动前一次执行完成的 之后,除非提供 @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();
}
}
}
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)
}
}
}
}