How ZooKeeper Implements Distributed Locks with Sequential Nodes
Distributed locks prevent concurrent conflicts over shared resources in distributed systems, and this article explains ZooKeeper’s approach using ordered sequential nodes, illustrating the lock acquisition and release process with command-line examples and a complete Java implementation.
Scenario
In a distributed system multiple subsystems may need to modify the same data concurrently, leading to conflicts because there is no logical ordering of operations.
To protect the shared resource a distributed lock is required.
ZooKeeper Solution
ZooKeeper stores data in a hierarchical tree similar to a file system, allowing creation of ordered nodes. When systems A, B, and C request a lock they each create an ordered sequential node under a common /Lock parent.
The node creation command is: create -s -e /Lock/data_A test Although each system runs the same command, the -s option creates an ordered node and the -e option creates an ephemeral node, so ZooKeeper assigns a unique sequence number (e.g., data_A00000000, data_A00000001, data_A00000002).
Each client reads the list of children of /Lock, sorts them, and the client whose node has the smallest sequence number acquires the lock and proceeds to use the shared resource.
When the client finishes, it deletes its node, causing ZooKeeper to notify the remaining clients. They re‑read the list, and the next smallest node gains the lock. Because the nodes are ephemeral, if a client crashes ZooKeeper automatically removes its node, preventing deadlocks.
Example Code
The following Java class demonstrates a simple distributed lock using ZooKeeper’s EPHEMERAL_SEQUENTIAL mode.
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
public class DistributedLock {
private final ZooKeeper zk;
private final String lockBasePath;
private final String lockName;
private String lockPath;
public DistributedLock(ZooKeeper zk, String lockBasePath, String lockName) {
this.zk = zk;
this.lockBasePath = lockBasePath;
this.lockName = lockName;
}
public void lock() throws IOException {
try {
lockPath = zk.create(lockBasePath + "/" + lockName, null, Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL_SEQUENTIAL);
final Object lock = new Object();
synchronized (lock) {
while (true) {
List<String> nodes = zk.getChildren(lockBasePath, new Watcher() {
@Override
public void process(WatchedEvent event) {
synchronized (lock) {
lock.notifyAll();
}
}
});
Collections.sort(nodes);
if (lockPath.endsWith(nodes.get(0))) {
return;
} else {
lock.wait();
}
}
}
} catch (KeeperException e) {
throw new IOException(e);
} catch (InterruptedException e) {
throw new IOException(e);
}
}
public void unlock() throws IOException {
try {
zk.delete(lockPath, -1);
lockPath = null;
} catch (KeeperException e) {
throw new IOException(e);
} catch (InterruptedException e) {
throw new IOException(e);
}
}
}Images in the original article illustrate the node creation, ordering, and lock release steps.
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.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
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.
