Implementing Distributed Locks with Redis, Redisson, and Zookeeper in Java
This article explains the principles and practical implementations of distributed locks using Redis (SETNX and SET with expiration), Redisson's Java client, and Zookeeper's sequential ephemeral nodes, providing code examples, lock acquisition and release mechanisms, and a comparison of their advantages and drawbacks.
In modern clustered deployments, local locks become insufficient, so distributed locks are needed to ensure that only one service instance can execute a critical section at a time.
Redis Implementation
Redis provides the SETNX command (set if not exists) and the combined SET key value EX seconds NX command to acquire a lock atomically with an expiration time, preventing deadlocks caused by process crashes.
private static final String LOCK_SUCCESS = "OK";
private static final Long RELEASE_SUCCESS = 1L;
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
@Override
public String acquire() {
try {
long end = System.currentTimeMillis() + acquireTimeout;
String requireToken = UUID.randomUUID().toString();
while (System.currentTimeMillis() < end) {
String result = jedis.set(lockKey, requireToken, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return requireToken;
}
try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}
} catch (Exception e) {
log.error("acquire lock due to error", e);
}
return null;
}
@Override
public boolean release(String identify) {
if (identify == null) { return false; }
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
try {
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(identify));
if (RELEASE_SUCCESS.equals(result)) {
log.info("release lock success, requestToken:{}", identify);
return true;
}
} catch (Exception e) {
log.error("release lock due to error", e);
} finally {
if (jedis != null) { jedis.close(); }
}
log.info("release lock failed, requestToken:{}, result:{}", identify, result);
return false;
}The Lua script guarantees atomic check‑and‑delete when releasing the lock.
Redisson Implementation
Redisson wraps Redis and offers a Java API similar to java.util.concurrent.locks.Lock. After adding the Redisson dependency, a lock can be obtained and used as follows:
private void test() {
RLock lock = redissonClient.getLock("test_lock");
lock.lock();
try {
// business logic
} finally {
lock.unlock();
}
}Redisson also supports read‑write locks, fair locks, and the RedLock algorithm. By default it provides a watchdog that automatically renews the lock every 10 seconds, keeping the lock alive as long as the holder is active.
Zookeeper Implementation
Zookeeper can be used to create temporary sequential nodes; the smallest node holds the lock. Other clients watch the predecessor node and acquire the lock when it disappears, avoiding the “herd effect”.
public class ZooKeeperDistributedLock implements Watcher {
private ZooKeeper zk;
private String locksRoot = "/locks";
private String productId;
private String waitNode;
private String lockNode;
private CountDownLatch latch;
private CountDownLatch connectedLatch = new CountDownLatch(1);
private int sessionTimeout = 30000;
public ZooKeeperDistributedLock(String productId) {
this.productId = productId;
try {
String address = "192.168.189.131:2181,192.168.189.132:2181";
zk = new ZooKeeper(address, sessionTimeout, this);
connectedLatch.await();
} catch (Exception e) { throw new LockException(e); }
}
@Override
public void process(WatchedEvent event) {
if (event.getState() == KeeperState.SyncConnected) { connectedLatch.countDown(); return; }
if (this.latch != null) { this.latch.countDown(); }
}
public void acquireDistributedLock() {
try {
if (this.tryLock()) { return; }
else { waitForLock(waitNode, sessionTimeout); }
} catch (Exception e) { throw new LockException(e); }
}
public boolean tryLock() {
try {
lockNode = zk.create(locksRoot + "/" + productId, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
List<String> locks = zk.getChildren(locksRoot, false);
Collections.sort(locks);
if (lockNode.equals(locksRoot + "/" + locks.get(0))) { return true; }
int previousLockIndex = -1;
for (int i = 0; i < locks.size(); i++) {
if (lockNode.equals(locksRoot + "/" + locks.get(i))) { previousLockIndex = i - 1; break; }
}
this.waitNode = locks.get(previousLockIndex);
} catch (Exception e) { throw new LockException(e); }
return false;
}
private boolean waitForLock(String waitNode, long waitTime) throws InterruptedException, KeeperException {
Stat stat = zk.exists(locksRoot + "/" + waitNode, true);
if (stat != null) {
this.latch = new CountDownLatch(1);
this.latch.await(waitTime, TimeUnit.MILLISECONDS);
this.latch = null;
}
return true;
}
public void unlock() {
try {
System.out.println("unlock " + lockNode);
zk.delete(lockNode, -1);
lockNode = null;
zk.close();
} catch (Exception e) { e.printStackTrace(); }
}
public static class LockException extends RuntimeException {
public LockException(String e) { super(e); }
public LockException(Exception e) { super(e); }
}
}Summary and Comparison
Redis implements a lock by inserting a key with an expiration; Zookeeper uses a temporary sequential node.
When a server crashes, Redis relies on key expiration, while Zookeeper automatically removes the temporary node.
Redis clients often spin‑wait for the lock, which can waste CPU; Zookeeper clients use watchers, offering better performance under contention.
The choice between Redis and Zookeeper should depend on the specific project’s technology stack, latency requirements, and failure‑handling strategy.
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.
Selected Java Interview Questions
A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!
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.
