Implementing a Robust Redis Distributed Lock with Spring Boot
The article shows how to build a reliable Redis‑based distributed lock in a Spring Boot 2.7 application by storing a unique UUID token, acquiring the lock with SET NX PX, releasing it atomically via a Lua script, and providing a clean Lock interface, factory component, and JUnit example.
When a single‑machine application can rely on Java's JUC locks, a distributed system needs a mechanism to guarantee that only one JVM process accesses a shared resource at a time. This is the purpose of a distributed lock.
The naive implementation uses SET lockKey 1 NX PX expireTime to acquire the lock and DEL lockKey to release it. Because the lock value does not contain a unique identifier, a client may delete a lock that was already re‑acquired by another client after the original lock timed out, leading to the "release other's lock" problem.
The correct solution stores a unique token (e.g., UUID:threadId ) as the lock value. When unlocking, the client first checks whether the stored value matches its token; only then is the lock deleted. This prevents accidental removal of another client’s lock.
To make the check‑and‑delete operation atomic, a Lua script is used: if (redis.call('exists', KEYS[1]) == 0) then return nil; end; if (redis.call('get', KEYS[1]) == ARGV[1]) then return redis.call('del', KEYS[1]); else return 0; end; The script returns 1 on successful unlock, 0 on mismatch, and nil if the key does not exist.
The tutorial also shows how to set up a Spring Boot 2.7 project with the spring-boot-starter-data-redis and Lombok dependencies, configure application.yml for Redis (host, port, password, Lettuce pool), and customize RedisTemplate to use JSON serialization.
A simple Lock interface is defined with tryLock , lock , and unlock methods. The DistributedLock class implements this interface, generating a UUID token, building the lock key, and using StringRedisTemplate.opsForValue().setIfAbsent for acquisition. The tryLock method supports a waiting timeout and optional spin‑wait, while lock blocks until the lock is obtained.
The unlock method executes the Lua script via DefaultRedisScript and throws an IllegalMonitorStateException if the lock is not owned by the current thread.
Finally, a RedisLockClient component provides a convenient getLock(String name) factory method, and a JUnit test demonstrates acquiring the lock, performing business logic, and releasing the lock in a finally block.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.