Search code examples
javaspringrandomschedule

Is it possible to schedule a job with Spring @Scheduled annotation to run every hour but each time at random hour?


I would like to run my task/method each hour.. but each time at random minute. I've already tried Spring @Scheduled to be started every day at a random minute between 4:00AM and 4:30AM but this solution is setting random initial value but after that same minute is used.

I would like to achieve a situation where the job is running like this. ex:

8:10 9:41 10:12 ...


Solution

  • Right, so...this isn't a schedule. This is a non-deterministic event.

    A scheduled event is something which is repeatable and something which can be consistently fired at a specific time. There's an order and predictability which go hand-in-hand with this.

    By having a job fire off at a given hour but not necessarily at a given minute, you lose predictability which is what the @Scheduled annotation would enforce (not necessarily through implementation, but as a side-effect; annotations may only contain compile-time constants and cannot dynamically change during runtime).

    As for a solution, Thread.sleep is brittle and will cause your entire application to sleep for that period of time which isn't what you want to do. Instead, you could wrap your critical code in a non-blocking thread and schedule that instead.

    Warning: Untested code below

    @Scheduled(cron = "0 0 * * * ?")
    public void executeStrangely() {
        // Based on the schedule above,
        // all schedule finalization should happen at minute 0.
        // If the pool tries to execute at minute 0, there *might* be
        // a race condition with the actual thread running this block.
        // We do *not* include minute 0 for this reason.
        Random random = new Random();
        final int actualMinuteOfExecution = 1 + random.nextInt(59);
        final ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);
    
        exec.schedule(() -> {
            // Critical code here
        }, actualMinuteOfExecution, TimeUnit.MINUTES);
    }
    

    I leave the effort of managing resources in a thread-safe manner as an exercise for the reader.