If you’re building applications that run on multiple instances, you’ll need a reliable way to ensure your scheduled tasks don’t execute simultaneously across different nodes.
This comprehensive guide will walk you through implementing ShedLock in your Spring Boot application, demonstrating how to prevent duplicate scheduled task execution in distributed environments.
You’ll learn everything from basic setup to advanced configurations, using Spring Boot and ShedLock.
Understanding Distributed Locking
Distributed locking is a mechanism you’ll need when running scheduled tasks across multiple application instances.

When you deploy your Spring Boot application on several servers, you need to ensure that scheduled tasks run only once at a given time.
This is where ShedLock comes in, providing a robust distributed lock that prevents your scheduled tasks from executing simultaneously across different nodes.
ShedLock makes sure that your scheduled tasks are executed at most once at the same time.
Configuring Maven/Gradle Dependencies
If you’re using Maven for your project, you’ll need to add ShedLock dependencies to your pom.xml or build.gradle file.
The core dependency is shedlock-spring
and a storage provider of your choice.
Setup your Maven dependencies with the following configuration:
// MAVEN <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-spring</artifactId> <version>6.3.0</version> </dependency> <dependency> <groupId>net.javacrumbs.shedlock</groupId> <artifactId>shedlock-provider-jdbc-template</artifactId> <version>6.3.0</version> </dependency> // GRADLE implementation 'net.javacrumbs.shedlock:shedlock-spring:6.3.0' implementation 'net.javacrumbs.shedlock:shedlock-provider-jdbc-template:6.3.0'
With ShedLock, you need to configure a lock provider in your Spring Boot application.
The most common approach is using a database-based lock provider.
Setting Up Database Connectivity
If you’re using a database-based lock provider, you’ll need to configure your database connection in application.properties
or application.yml
file.
spring: datasource: url:jdbc:postgresql://localhost:5432/your_database username:your_username password: your_password
Also, you need to have corresponding database related dependencies in your pom.xml or build.gradle.
Refer how to use spring data jpa for database related operations.
You also need to create a table in your application database for which the following database respective queries can be used.
# MySQL, MariaDB CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name)); # Postgres CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP NOT NULL, locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name)); # Oracle CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until TIMESTAMP(3) NOT NULL, locked_at TIMESTAMP(3) NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name)); # MS SQL CREATE TABLE shedlock(name VARCHAR(64) NOT NULL, lock_until datetime2 NOT NULL, locked_at datetime2 NOT NULL, locked_by VARCHAR(255) NOT NULL, PRIMARY KEY (name)); # DB2 CREATE TABLE shedlock(name VARCHAR(64) NOT NULL PRIMARY KEY, lock_until TIMESTAMP NOT NULL, locked_at TIMESTAMP NOT NULL, locked_by VARCHAR(255) NOT NULL);
This is required since ShedLock uses database to manage locks.
ShedLock Architecture and Components
Fundamentals of ShedLock revolve around three main components:
LockProvider
interface, which manages lock acquisition and release;@SchedulerLock
annotation, which you’ll use to configure lock parameters; andLockConfiguration
class, which holds lock-specific settings.
You can choose from various lock providers including JDBC, Redis, MongoDB, and others to store lock information.
Defining Scheduled Tasks
To implement scheduled tasks in your Spring Boot application, you’ll need to define methods annotated with @Scheduled
.
These tasks can be configured with fixed rates, delays, or cron expressions.
You’ll start by enabling scheduling in your application using @EnableScheduling
annotation at the configuration class level.
This setup forms the foundation for implementing distributed locks with ShedLock.
Implementing ShedLock on Scheduled Methods
Any scheduled task that requires distributed locking can be secured using the @SchedulerLock
annotation.
You’ll need to specify a unique name for each lock and set appropriate lock parameters like lockAtMostFor
and lockAtLeastFor
to control lock duration and prevent task overlap.
A typical implementation includes both @Scheduled
and @SchedulerLock
annotations on your method.
The lockAtMostFor
parameter ensures the lock is automatically released after the specified duration, preventing deadlocks if your application crashes.
You can set these values based on your task’s expected execution time.
@Scheduled(cron = "0 */15 * * * *") // runs every 15 mins @SchedulerLock(name = "TaskName_scheduledTask", lockAtMostFor = "14m", lockAtLeastFor = "5m") public void scheduledTask() { // Your task implementation }
In this example,
The @Scheduled
annotation is part of Spring Framework’s task scheduling capabilities.
It allows you to execute methods at specified intervals without manual intervention.
cron = “0 */15 * * * *”
defines when the task will run using a cron expression
This specific expression translates to: “Run at 0 seconds, every 15 minutes, every hour, every day, every month, every day of the week”
@Scheduled
annotation is perfect for recurring tasks like data cleanup, report generation, or periodic data processing.
The @SchedulerLock
annotation comes from the ShedLock library, which provides distributed locking for scheduled tasks.
Key parameters
name = “TaskName_scheduledTask”
– Provides a unique identifier for this scheduled task
– Must be unique across your entire application to prevent lock conflicts
lockAtMostFor = “14m”
– Sets the maximum duration the lock will be held
– After 14 minutes, the lock will be automatically released even if the task is still running
– This prevents deadlocks if a task instance crashes without releasing the lock
– Set to slightly less than the scheduling interval (15 minutes) to prevent overlap
lockAtLeastFor = “5m”
– Defines the minimum time the lock will be held
– Ensures the lock won’t be released for at least 5 minutes, even if the task completes earlier
– Prevents task execution by other nodes if the task completes very quickly
– Useful for preventing double execution in highly distributed environments
Remember that only methods that have
@SchedulerLock
annotation are locked and will be the candidates for being run on a single application instance.
Setting Up a Lock Provider
With ShedLock, you need to configure a lock provider in your Spring Boot application.
The most common approach is using a database-based lock provider:
@Configuration @EnableSchedulerLock(defaultLockAtMostFor = "10m") public class ShedLockConfig { @Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider(dataSource); } }
Configuration of the lock provider requires careful consideration of your infrastructure.
You can choose from various providers including JDBC, MongoDB, Redis, or others based on your application’s architecture.
Each provider has specific configuration requirements and performance characteristics.
Customizing Lock Provider
Above configuration creates a lock provider with default configuration.
But, you can also customize lock provider configuration such as the datasource, name of database table, names of columns etc., as shown below.
@Bean public LockProvider lockProvider(DataSource dataSource) { return new JdbcTemplateLockProvider( JdbcTemplateLockProvider. Configuration. builder(). withJdbcTemplate(new JdbcTemplate(dataSource)). withTableName("shedlock"). withTimeZone(TimeZone.getTimeZone("UTC")). withColumnNames( new ColumnNames("name", "lock_until", "locked_at", "locked_by")). build() ); }
Benefits of Using ShedLock in Spring Boot
ShedLock offers you a reliable solution for handling distributed scheduled tasks in your Spring Boot applications.
You get
- automatic lock management,
- support for various storage providers, and
- seamless integration with Spring’s scheduling mechanism.
With minimal configuration, you can prevent task overlapping and ensure consistent execution across your distributed system.
Testing ShedLock Implementation
Testing of ShedLock can be performed by running multiple instances of your application simultaneously.
You’ll observe that only one instance executes the scheduled task while others wait for lock release.
This behavior ensures task execution isolation across your distributed system.
Methods for testing lock behavior include monitoring lock tables in your database, checking application logs for lock acquisition patterns, and verifying task execution timestamps across different application instances.
Integrating ShedLock with Distributed Systems
Lock management in distributed environments requires careful coordination.
You can integrate ShedLock with various distributed storage systems like Redis or MongoDB to ensure consistent locking across your entire application cluster.
@Configuration public class ShedLockConfig { @Bean public LockProvider lockProvider(MongoClient mongo) { return new MongoLockProvider( mongo.getDatabase("scheduler")); } }
You can also implement custom health checks and monitoring to detect and respond to lock-related issues across your distributed infrastructure as shown below
@Configuration public class ShedLockConfig { @Bean public HealthIndicator healthIndicator(LockProvider lockProvider) { return new ShedLockHealthIndicator(lockProvider); } }
Monitoring Lock Performance
The most effective way to monitor ShedLock performance is by implementing metrics collection and alerting systems.
You can use Spring Boot Actuator to expose lock-related metrics and integrate with monitoring tools like Prometheus and Grafana.
Issues with lock performance can be identified early by setting up proper monitoring dashboards.
You should track metrics such as lock acquisition time, lock duration, and failed attempts.
Here’s an example of implementing custom metrics:
@Component public class ShedLockMetrics { private final MeterRegistry registry; public ShedLockMetrics(MeterRegistry registry) { this.registry = registry; } public void recordLockAcquisition(String taskName, long duration) { registry. timer("shedlock.acquisition", "task", taskName). record(duration, TimeUnit.MILLISECONDS); } }
Summing up
You’ve learned how to effectively implement distributed task scheduling using Spring Boot and ShedLock.
Your applications can now avoid task overlapping by implementing locks with Spring Boot and ShedLock library.
You can ensure your scheduled tasks run efficiently across multiple instances.
This knowledge empowers you to build more reliable and scalable distributed systems.