Spring Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

Spring provides an easy to implement API for scheduling jobs. It works great until we deploy multiple instances of our application. Spring by default cannot handle scheduler synchronization over multiple instances and executes the jobs simultaneously on every node instead.

In this short tutorial, we’ll look at ShedLock – a Java library that makes sure our scheduled tasks run only once at the same time and is an alternative to Quartz.

2. Maven Dependencies

To use ShedLock with Spring we need to add the shedlock-spring dependency:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-spring</artifactId>
    <version>2.2.0</version>
</dependency>

3. Configuration

Note that ShedLock works only in environments with a shared database. It creates a table or document in the database where it stores the information about the current locks.

Currently, ShedLock supports Mongo, Redis, Hazelcast, ZooKeeper, and anything with a JDBC driver.

For this example, we’ll use a PostgreSQL database. To make it work we need to provide ShedLock’s JDBC dependency:

<dependency>
    <groupId>net.javacrumbs.shedlock</groupId>
    <artifactId>shedlock-provider-jdbc-template</artifactId>
    <version>2.1.0</version>
</dependency>

Next, we need to create a database table for ShedLock to keep information about scheduler locks:

CREATE TABLE shedlock(
  name VARCHAR(64),
  lock_until TIMESTAMP(3) NULL,
  locked_at TIMESTAMP(3) NULL,
  locked_by  VARCHAR(255),
  PRIMARY KEY (name)
)

Another configuration requirement we have to provide are the @EnableScheduling and @EnableSchedulerLock annotations on our Spring configuration class:

@SpringBootApplication
@EnableScheduling
@EnableSchedulerLock(defaultLockAtMostFor = "PT30S")
public class SpringApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringApplication.class, args);
    }
}

The defaultLockAtMostFor parameter specifies the default amount of time the lock should be kept in case the executing node dies. It uses the ISO8601 Duration format.

In the next section, we’ll see how to override this default.

4. Creating Tasks

To create a scheduled task handled by ShedLock, we simply put the @Scheduled and @SchedulerLock annotations on a method:

@Component
class TaskScheduler {

    @Scheduled(cron = "*/15 * * * *")
    @SchedulerLock(name = "TaskScheduler_scheduledTask", 
      lockAtLeastForString = "PT5M", lockAtMostForString = "PT14M")
    public void scheduledTask() {
        // ...
    }
}

First, let’s look at @Scheduled. It supports the cron format, with this expression meaning “every 15 minutes”.

Next, taking a look at @SchedulerLock, the name parameter has to be unique and ClassName_methodName is typically enough to achieve that. We don’t want more than one run of this method happening at the same time, and ShedLock uses the unique name to achieve that.

We’ve also added a couple of optional parameters.

First, we’ve added lockAtLeastForString so that we can put some distance between method invocations. Using “PT5M” means that this method will hold the lock for 5 minutes, at a minimum. In other words, that means that this method can be run by ShedLock no more often than every five minutes.

Next, we added lockAtMostForString to specify how long the lock should be kept in case the executing node dies. Using “PT14M” means that it will be locked for no longer than 14 minutes.

In normal situations, ShedLock releases the lock directly after the task finishes. Now, really, we didn’t have to do that because there is a default provided in @EnableSchedulerLock, but we’ve chosen to override that here.

5. Conclusion

In this article, we’ve learned how to create and synchronize scheduled tasks using ShedLock.

As always all source code is available over on GitHub.

Spring bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

newest oldest most voted
Notify of
Tobias
Guest
Tobias

Hello.

Great article. Small remark. The cron pattern you defined is not every 15 minutes, but every 15 seconds 🙂

Loredana Crusoveanu
Editor

Hey Tobias,

Thanks, fixed. There was an extra * there 🙂

Cheers.

Rohit Kumar
Guest
Rohit Kumar

Hi,
can we take cron value dynamic , my requirement is that i have capture value for crone and scheduler will run.
@Scheduled(cron = “*/15 * * * * *”)
like @Scheduled(cron = “*/x * * * * *”)

Thanks in Advance

Loredana Crusoveanu
Editor

Hi Rohit,

As I mentioned over email, you have several options. You can set different time expressions in the cron: https://www.baeldung.com/cron-expressions

Also, you can load the expression from a properties file as shown in section 8 here: https://www.baeldung.com/spring-scheduled-tasks

Otherwise, if you want more control over the scheduling, have a look at the TaskScheduler class: https://www.baeldung.com/spring-task-scheduler Using this, you can schedule a task programmatically, instead of using the @Scheduled annotation.

Cheers,
Team