介绍
在分布式系统中,为了保证数据的一致性和避免并发冲突,常常需要使用分布式锁来对共享资源进行保护。本文将介绍如何使用Java语言来实现分布式锁,并提供基于Redis和Zookeeper的两种实现方式。
Redis实现
什么是Redis
Redis是一个基于内存的高性能键值存储数据库,它支持多种数据结构,如字符串、哈希、列表、集合和有序集合。Redis具有高速读写、持久化、复制、事务、发布订阅和备份等功能,被广泛应用于分布式系统中。
Redis分布式锁实现思路
在Redis中,我们可以通过使用SETNX命令来实现分布式锁。SETNX命令可以在键不存在时,将键的值设置为指定的字符串。我们可以将锁的获取过程看作是尝试给指定的键设置值的过程,如果设置成功,则表示获取到了锁,否则表示锁已经被其他客户端获取。
分布式锁的实现步骤
- 确定一个全局唯一的锁标识,作为Redis中的键。
- 执行SETNX命令,将锁的标识作为键,当前时间戳作为值,设置给Redis。
- 检查SETNX命令的返回值,如果返回1,则表示成功获取到了锁;如果返回0,则表示锁已经被其他客户端获取。
- 如果成功获取到了锁,则可以执行需要保护的代码逻辑。
- 当需要释放锁时,可以通过DEL命令将锁移除。
Redis分布式锁的代码示例
public class RedisDistributedLock {
private RedisTemplate<String, String> redisTemplate;
public RedisDistributedLock(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
public boolean lock(String lockKey) {
// 设置锁的过期时间,防止锁忘记释放
long lockExpireTime = 5 * 1000;
long startTime = System.currentTimeMillis();
try {
// 获取锁的时间戳
String lockValue = String.valueOf(startTime);
Boolean result = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, lockExpireTime, TimeUnit.MILLISECONDS);
if (result != null && result) {
return true;
}
// 未获取到锁,等待一段时间后重试
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
return false;
}
public void unlock(String lockKey) {
redisTemplate.delete(lockKey);
}
}
ZooKeeper实现
什么是ZooKeeper
ZooKeeper是一个高性能的分布式协调服务,它提供分布式锁、选举、配置管理和命名服务等功能。ZooKeeper将所有的数据保存在内存中,并通过可靠存储和复制机制确保数据的持久性和一致性。
Zookeeper分布式锁实现思路
在ZooKeeper中,我们可以使用节点的创建与删除来实现分布式锁。当多个客户端同时尝试创建同一个节点时,只有一个客户端能够成功创建节点,其余的客户端将会阻塞等待。当拥有锁的客户端操作完成后,会删除该节点,从而释放锁。
分布式锁的实现步骤
- 创建一个持久节点,作为锁的父节点。
- 执行create命令创建一个临时顺序节点,作为当前客户端的锁节点。
- 获取该父节点的所有子节点,并按照节点名称的顺序进行排序。
- 检查当前客户端的锁节点是否为子节点列表中的第一个节点,如果是,则表示获取到了锁,否则,则监听前一个顺序节点的删除事件,等待锁的释放。
- 当需要释放锁时,删除该客户端的锁节点。
ZooKeeper分布式锁的代码示例
public class ZooKeeperDistributedLock {
private ZooKeeper zkClient;
private String lockPath;
private String lockNode;
private CountDownLatch countDownLatch;
public ZooKeeperDistributedLock(String connectString, String lockPath) {
this.lockPath = lockPath;
try {
zkClient = new ZooKeeper(connectString, 5000, event -> {
if (event.getState() == Watcher.Event.KeeperState.SyncConnected) {
countDownLatch.countDown();
}
});
countDownLatch = new CountDownLatch(1);
countDownLatch.await();
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
public boolean lock() {
try {
// 创建锁节点
lockNode = zkClient.create(lockPath + "/lock_", null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
List<String> nodes = zkClient.getChildren(lockPath, false);
Collections.sort(nodes);
String currentNode = lockNode.substring(lockNode.lastIndexOf("/") + 1);
if (currentNode.equals(nodes.get(0))) {
return true;
}
String prevNode = nodes.get(nodes.indexOf(currentNode) - 1);
Stat stat = zkClient.exists(lockPath + "/" + prevNode, event -> {
if (event.getType() == Watcher.Event.EventType.NodeDeleted) {
countDownLatch.countDown();
}
});
if (stat != null) {
countDownLatch.await();
return true;
}
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
public void unlock() {
try {
zkClient.delete(lockNode, -1);
zkClient.close();
} catch (KeeperException | InterruptedException e) {
e.printStackTrace();
}
}
}
总结
本文介绍了使用Java语言实现分布式锁的方法,并提供了基于Redis和Zookeeper的两种实现方式。通过使用分布式锁,我们可以保证在分布式系统中共享资源的一致性和避免并发冲突。在选择具体的分布式锁实现方式时,可以根据具体的业务需求和系统环境进行选择。
本文来自极简博客,作者:云端之上,转载请注明原文链接:使用Java进行分布式锁的实现:基于Redis与Zookeeper