Roni Koren Kurtberg
Roni Koren Kurtberg

Reputation: 515

Thread.sleep within @Scheduled method in Spring

I have a method that is scheduled to run each X ms.

This method is launching a new method within a new thread.

Nevertheless, I want to delay this method before counting again.

Here is my code:

@Scheduled(fixedRate = RATE_IN_MS)
public void myMethod(){
    new Thread(() -> method()).start();
    Thread.sleep(RATE_IN_MS);
}

The problem is that the behavior is not like I want.

Here is the current behavior:

  1. After RATE_IN_MS myMethod is launched.
  2. method() start running in a different thread.
  3. After RATE_IN_MS myMethod is launched.

The problem: Why does my sleep method didn't delay the next launch of myMethod? RATE_IN_MS (by fixedDelay) + RATE_IN_MS (because of Thread.sleep(RATE_IN_MS) need to be the real delay between them.

What I'm missing here?

Thanks!

Upvotes: 2

Views: 5536

Answers (3)

admdev
admdev

Reputation: 468

When using fixedrate scheduling, there are two possible situations as to when the next invocation of myMethod occurs:

  1. if after RATE_IN_MS milliseconds from the last myMethod invocation, myMethod is no longer running, then that's when myMethod will be invoked again.
  2. if after RATE_IN_MS milliseconds from the last myMethod invocation that invocation is still running and it hasn't returned, then this will cause the next scheduled call to be blocked until the current invocation is over.

For example if RATE_IN_MS is 10 ms, let's consider two scenarios:

  1. myMethod takes 5 ms to execute. In this case, a new myMethod invocation will occur every 10 ms because 5 ms is less than 10 ms.
  2. if for some reason the last myMethod invocation took 12 ms to complete, which is longer than 10 ms, then the next one will be queued. So there will be a minimum of 12 ms between invocation which is more than RATE_IN_MS.

Now to answer your question specifically, let's say that RATE_IN_MS is 10 ms, then definitely we have the second case from my above example, however, the wait time will not be multiplied in two as you expect, rather it will be consisted of two time periods:

  1. the time it took myMethod to reach Thread.sleep(RATE_IN_MS) which could be much less than RATE_IN_MS, let's say it's only 3 ms and let's call this time X.
  2. the time the Thread sleeps which is RATE_IN_MS which we are saying it's 10 ms for example.

Then the total time between invocations of myMethod() will be: X+RATE_IN_MS which according to my example is 13 ms and not RATE_IN_MS * 2.

Upvotes: 0

mindSet
mindSet

Reputation: 241

I have created this running example for your reference.Execution delay is configured in spring configuration file which can be changed as you wish.

package com.myprgm;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

@Component
@EnableScheduling
public class JobSchdulingServc {
    @Value("${com.executionDelay}")
    private long executionDelay; // in milliseconds,mention in sping property file
    @Autowired
    TaskScheduler taskScheduler;

    @Bean
    public TaskScheduler poolScheduler() {
        return new ThreadPoolTaskScheduler();
    }

    private void stopSceduling(boolean terminateJob) {
        ((ThreadPoolTaskScheduler) taskScheduler).shutdown();
        if (terminateJob) {
            System.exit(1);
        }

    }
    public void scheduleMyJob() {
        taskScheduler.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    someServiceimpl.generateMyDataAndSendInEveryMilliSeconds(); // service you want to execute after
                    // specified executionDelay
                } catch (Exception e) {
                    stopSceduling(true);
                }
            }

        }, executionDelay);
    }

}

Upvotes: 2

xingbin
xingbin

Reputation: 28279

With fixedRate, the scheduler does not care the time spent in myMethod.

You can use @Scheduled(fixedDelay = RATE_IN_MS). The scheduler will calculate next execution time after finishing myMethod.

Upvotes: 1

Related Questions