1

I have following job in spring boot application:

@Scheduled(cron = "0/5 * * * * *")
fun processDocumentsBatch() {
    log.info("job has started")
    Thread.sleep(7000)
    log.info("job has finished")
}

I see logs like this:

2025-05-28T13:26:10.010+03:00  INFO 23424 --- [   scheduling-1] Service : job has started
2025-05-28T13:26:17.035+03:00  INFO 23424 --- [   scheduling-1] Service : job has finished
2025-05-28T13:26:20.005+03:00  INFO 23424 --- [   scheduling-1] Service : job has started
2025-05-28T13:26:27.041+03:00  INFO 23424 --- [   scheduling-1] Service : job has finished
2025-05-28T13:26:30.012+03:00  INFO 23424 --- [   scheduling-1] Service : job has started
2025-05-28T13:26:37.036+03:00  INFO 23424 --- [   scheduling-1] Service : job has finished
2025-05-28T13:26:40.003+03:00  INFO 23424 --- [   scheduling-1] Service : job has started
2025-05-28T13:26:47.031+03:00  INFO 23424 --- [   scheduling-1] Service : job has finished

I expected that in case of overlap the task will be put in a queue and it wil be taken for execution as soon as previous one is finished but looks like it is just skipped.

Based on this spring uses single thread executor with queue for scheduled tasks.

In Jdk I see this one and it has a queue.

    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new AutoShutdownDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }
    

So could you please clarify why job is cancelled ?

Is there way to change this behaviour to pospoone the task instead of cancelling ? Is there any configuration key for that purpose ?

2
  • Did you try @Scheduled(fixedRate = 5000)? Commented Jun 1 at 19:15
  • @georgii you are right. But my question about cron. I am curious to know. Commented Jun 1 at 22:10

1 Answer 1

1

Why is my scheduled task getting skipped instead of queued?

This happens because:

Spring’s cron scheduler doesn't queue overlapping executions—it simply skips if the previous run isn’t finished.

Although Spring uses a ScheduledExecutorService internally (which supports a queue), cron-based tasks don’t use that queue. They are executed periodically based on fixed delay or cron expressions, and subsequent triggers are ignored if the previous task hasn’t completed.

  • Recommended Solution: Use @Async With a Queued Executor (this will make it parallel also)

  • Alternatively - you could implement your own simple LinkedBlockingQueue below.

@Component
class DocumentProcessor(
    private val taskExecutor: Executor
) {

    private val queue = LinkedBlockingQueue<Unit>()

    @Scheduled(cron = "0/5 * * * * *")
    fun scheduleTrigger() {
        queue.offer(Unit)
        processQueue()
    }

    @Synchronized
    private fun processQueue() {
        if (queue.isNotEmpty()) {
            taskExecutor.execute {
                while (true) {
                    val item = queue.poll(100, TimeUnit.MILLISECONDS) ?: break
                    processDocumentsBatch()
                }
            }
        }
    }

    private fun processDocumentsBatch() {
        log.info("job has started")
        Thread.sleep(7000)
        log.info("job has finished")
    }
}
  • Thirdly, you could overkill by using a production grade task lib - Quartz for Advanced Scheduling
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.