# How to Implement Distributed Locks with Redisson in Spring Boot Scheduled Tasks

> Seamlessly implement distributed locks with Redisson in Spring Boot scheduled tasks. Secure your jobs and prevent duplicate execution using tryLock within a try-with-resources block.

- Repository: [OpenContiNew/continew-admin](https://github.com/continew-org/continew-admin)
- Tags: how-to-guide
- Published: 2026-02-28

---

**Wrap your `@Scheduled` method body in a `try-with-resources` block using `RedisLockUtils.tryLock(lockKey)` to ensure only one cluster instance executes the job at a time.**

When running Spring Boot applications in a clustered environment, `@Scheduled` tasks execute on every node simultaneously, causing duplicate processing or race conditions. In the **ContiNew Admin** project, the `continew-starter-cache-redisson` module provides a lightweight utility—`RedisLockUtils`—that wraps Redisson’s `RLock` to implement distributed locks with automatic resource management. This guide demonstrates how to implement distributed locks with Redisson in scheduled tasks using patterns already established in the `continew-org/continew-admin` codebase.

## Understanding the RedisLockUtils Wrapper

The `RedisLockUtils` class (located in [`continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisLockUtils.java`](https://github.com/continew-org/continew-admin/blob/main/continew-starter-cache-redisson/src/main/java/top/continew/starter/cache/redisson/util/RedisLockUtils.java)) encapsulates Redisson’s distributed locking logic and implements `AutoCloseable`. This design allows developers to use Java’s `try-with-resources` syntax, ensuring the lock is released automatically—even if the business logic throws an exception.

Key characteristics of the utility:

- **Non-blocking acquisition**: `tryLock()` attempts to acquire the lock immediately without waiting.
- **Auto-release**: The lock unlocks automatically when the `try` block exits.
- **State inspection**: The `isLocked()` method indicates whether the current instance successfully acquired the lock.

## Implementing Distributed Locks in Scheduled Jobs

### Step-by-Step Pattern

Follow this four-step pattern to protect any `@Scheduled` method:

1. **Generate a unique lock key** that identifies the specific scheduled task (e.g., `"Lock:NoticePublishJob"`).
2. **Acquire the lock** using `RedisLockUtils.tryLock(lockKey)`, which returns a lock instance immediately.
3. **Validate lock acquisition** by calling `isLocked()`; if false, another instance holds the lock and the current thread should skip execution.
4. **Execute business logic** inside the locked block.

### Complete Implementation Example

The `NoticePublishJob` class in [`continew-server/src/main/java/top/continew/admin/job/NoticePublishJob.java`](https://github.com/continew-org/continew-admin/blob/main/continew-server/src/main/java/top/continew/admin/job/NoticePublishJob.java) provides a concrete example. The following adaptation adds distributed lock protection to the `publishNoticeWithSchedule` method:

```java
package top.continew.admin.job;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.cache.redisson.util.RedisLockUtils;

@Slf4j
@Component
@RequiredArgsConstructor
public class NoticePublishJob {

    @Component
    public static class Scheduler {

        @Scheduled(cron = "0 * * * * ?")
        @Transactional(rollbackFor = Exception.class)
        public void publishNoticeWithSchedule() {
            String lockKey = "Lock:NoticePublishJob";
            try (RedisLockUtils lock = RedisLockUtils.tryLock(lockKey)) {
                if (!lock.isLocked()) {
                    log.warn("Another instance is already publishing notices – skipping.");
                    return;
                }
                log.info("定时任务 [公告发布] 开始执行。");
                publishNotice();   // Original business logic
                log.info("定时任务 [公告发布] 执行结束。");
            }
        }
    }
    
    // Additional class methods...
}

```

**Critical implementation details:**

- The `lockKey` uses a `"Lock:"` prefix followed by the job name to avoid collisions with other Redis keys.
- The `try` block ensures the Redisson lock releases even if `publishNotice()` throws an exception.
- The early return pattern (`if (!lock.isLocked())`) prevents duplicate execution without blocking the thread.

## Reference Implementation in Service Layer

For components outside scheduled jobs, the same pattern applies. In [`continew-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java`](https://github.com/continew-org/continew-admin/blob/main/continew-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java), the `createParentDir` method demonstrates dynamic lock key construction for resource-specific locking:

```java
// Inside FileServiceImpl.createParentDir
String lockKey = StrUtil.format("Lock:{}:{}", storage.getCode(), parentPath);
try (RedisLockUtils lock = RedisLockUtils.tryLock(lockKey)) {
    if (!lock.isLocked()) {
        return; // 获取锁失败，直接返回
    }
    // Critical section: create directory structure exclusively
}

```

This example shows that lock keys can incorporate runtime variables (storage code and path) to allow parallel processing of unrelated resources while preventing conflicts on identical paths.

## Best Practices for Lock Key Design

Designing effective lock keys ensures both safety and performance in distributed systems:

- **Use a consistent prefix**: Start keys with `"Lock:"` to namespace them separately from cache entries and business data.
- **Include business identifiers**: For dynamic resources, incorporate unique IDs (like `storage.getCode()` and `parentPath`) to prevent global bottlenecks while maintaining safety for specific resources.
- **Keep keys short but descriptive**: Excessively long keys increase Redis memory usage and network overhead without adding value.

## Summary

- `RedisLockUtils.tryLock()` provides non-blocking, auto-releasing distributed locks via Redisson.
- Always check `isLocked()` before executing protected logic to handle lock contention gracefully.
- The `try-with-resources` pattern guarantees lock release, preventing deadlocks during application crashes or exceptions.
- ContiNew Admin implements this pattern in `NoticePublishJob` for scheduled tasks and `FileServiceImpl` for service-layer synchronization.

## Frequently Asked Questions

### What happens if the application crashes while holding a Redisson lock?

Redisson locks typically use a watchdog mechanism with a default lease time. If the JVM crashes or the instance becomes unavailable, Redis automatically expires the lock key after the lease duration expires, allowing other instances to acquire the lock. The `RedisLockUtils` wrapper maintains this safety mechanism, preventing permanent deadlocks.

### Can I use RedisLockUtils for non-scheduled business logic?

Yes. While this guide focuses on how to implement distributed locks with Redisson in scheduled tasks, the `FileServiceImpl.createParentDir` method demonstrates identical usage in standard service methods. Any code path requiring cluster-wide exclusivity—such as database migrations, file system operations, or inventory updates—can use this utility.

### How is RedisLockUtils different from using RedissonClient directly?

`RedisLockUtils` abstracts the boilerplate of obtaining an `RLock`, checking availability, and ensuring release. By implementing `AutoCloseable`, it eliminates manual `unlock()` calls and reduces the risk of deadlocks compared to raw Redisson client usage, where forgotten `finally` blocks can leave locks hanging indefinitely.

### Does the lock block other instances indefinitely?

No. `RedisLockUtils.tryLock()` attempts to acquire the lock immediately and returns either a valid lock instance or an empty holder. It does not block threads waiting for the lock to become available, making it suitable for scheduled tasks where skipping an execution is preferable to delaying it indefinitely.