Master Redis Distributed Locks in Java: Correct Implementation & Common Pitfalls
This article explains how to correctly implement a Redis-based distributed lock in Java, covering essential reliability requirements, step‑by‑step code for acquiring and releasing locks, and detailed analyses of common flawed approaches, ensuring atomicity and proper ownership handling.
Introduction
Distributed locks can be implemented in three main ways: database optimistic lock, Redis‑based lock, and ZooKeeper‑based lock. This article focuses on the second method, providing a correct Redis implementation and pointing out common mistakes found in many online examples.
Reliability Requirements
To ensure a distributed lock is reliable, it must satisfy four conditions:
Mutual exclusion : Only one client can hold the lock at any time.
No deadlock : If a client crashes while holding the lock, other clients must still be able to acquire it later.
Fault tolerance : The lock should work as long as a majority of Redis nodes are operational.
Owner awareness : The same client that acquired the lock must be the one to release it.
Code Implementation
Component Dependency
Add the Jedis library to your Maven pom.xml:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>Lock Acquisition
The correct way to acquire a lock uses a single atomic SET command with the NX and PX options:
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* Try to acquire a distributed lock.
* @param jedis Redis client
* @param lockKey Lock key
* @param requestId Unique identifier of the requester
* @param expireTime Expiration time in milliseconds
* @return true if the lock was acquired
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
}Explanation of the five parameters of jedis.set:
key : the lock identifier; it must be unique.
value : a requestId (e.g., UUID.randomUUID().toString()) that identifies the client that owns the lock.
NX : "SET IF NOT EXIST" – the command succeeds only when the key does not already exist, guaranteeing mutual exclusion.
PX : sets an expiration time for the key.
expireTime : the expiration duration in milliseconds.
Using this atomic command satisfies mutual exclusion, prevents deadlocks (the key expires automatically), and records the owner for safe release.
Common Mistake 1 – Non‑Atomic setnx + expire
public static void wrongGetLock1(Jedis jedis, String lockKey, String requestId, int expireTime) {
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// If the process crashes here, the lock has no expiration → deadlock
jedis.expire(lockKey, expireTime);
}
}This approach is not atomic; a crash between setnx and expire leaves the lock without a timeout.
Common Mistake 2 – Complex setnx with client‑generated expiration
public static boolean wrongGetLock2(Jedis jedis, String lockKey, int expireTime) {
long expires = System.currentTimeMillis() + expireTime;
String expiresStr = String.valueOf(expires);
if (jedis.setnx(lockKey, expiresStr) == 1) {
return true;
}
String currentValueStr = jedis.get(lockKey);
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
String oldValueStr = jedis.getSet(lockKey, expiresStr);
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
return true;
}
}
return false;
}Problems:
Clients must have synchronized clocks.
When multiple clients set the expiration simultaneously, one may overwrite another’s timeout.
The lock lacks an owner identifier, allowing any client to release it.
Unlock Code
Correct Unlock Using Lua
A Lua script guarantees atomic check‑and‑delete:
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
"return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
return Long.valueOf(1L).equals(result);
}The script returns 1 only when the stored value matches requestId, ensuring that only the lock owner can delete it.
Common Unlock Mistake 1 – Direct delete
public static void wrongReleaseLock1(Jedis jedis, String lockKey) {
jedis.del(lockKey);
}Any client can delete the lock, even if it does not own it.
Common Unlock Mistake 2 – Separate get and del
public static void wrongReleaseLock2(Jedis jedis, String lockKey, String requestId) {
if (requestId.equals(jedis.get(lockKey))) {
// Race condition: the lock may have been acquired by another client after the get
jedis.del(lockKey);
}
}If the lock expires after the get but before the del, a client may unintentionally delete another client’s lock.
Conclusion
This article demonstrated a reliable Java implementation of a Redis distributed lock, highlighted four essential reliability criteria, and presented two classic error patterns for both locking and unlocking. By using the atomic SET command with NX and PX, and a Lua script for release, developers can avoid deadlocks, ensure ownership, and achieve fault‑tolerant locking.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITFLY8 Architecture Home
ITFLY8 Architecture Home - focused on architecture knowledge sharing and exchange, covering project management and product design. Includes large-scale distributed website architecture (high performance, high availability, caching, message queues...), design patterns, architecture patterns, big data, project management (SCRUM, PMP, Prince2), product design, and more.
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.
