18.9.4 分布式锁实现方案与选择
1. 分布式锁基本原理
1.1 分布式锁核心要求
public class DistributedLockPrinciple {
/*
* 分布式锁基本要求:
*
* 1. 互斥性
* - 任意时刻只有一个客户端能持有锁
*
* 2. 安全性
* - 锁只能被持有者释放,防止误删
*
* 3. 可用性
* - 获取锁和释放锁必须高可用
*
* 4. 容错性
* - 具备锁失效机制,防止死锁
*
* 常见实现方案:
* - Redis分布式锁
* - Zookeeper分布式锁
* - 数据库分布式锁
* - Etcd分布式锁
*/
public void demonstrateDistributedLock() {
System.out.println("=== 分布式锁方案演示 ===");
demonstrateRedisLock();
demonstrateZookeeperLock();
demonstrateDatabaseLock();
}
private void demonstrateRedisLock() {
System.out.println("--- Redis分布式锁演示 ---");
RedisDistributedLock redisLock = new RedisDistributedLock("resource:lock");
System.out.println("1. 客户端A尝试获取锁:");
boolean lockA = redisLock.tryLock("clientA", 30000);
System.out.println(" 获取结果: " + lockA);
System.out.println("\n2. 客户端B尝试获取锁:");
boolean lockB = redisLock.tryLock("clientB", 30000);
System.out.println(" 获取结果: " + lockB);
System.out.println("\n3. 客户端A释放锁:");
boolean unlockA = redisLock.unlock("clientA");
System.out.println(" 释放结果: " + unlockA);
System.out.println("\n4. 客户端B再次尝试获取锁:");
boolean lockB2 = redisLock.tryLock("clientB", 30000);
System.out.println(" 获取结果: " + lockB2);
redisLock.unlock("clientB");
System.out.println("Redis分布式锁演示完成\n");
}
private void demonstrateZookeeperLock() {
System.out.println("--- Zookeeper分布式锁演示 ---");
ZookeeperDistributedLock zkLock = new ZookeeperDistributedLock("/locks/resource");
System.out.println("1. 客户端A获取锁:");
boolean lockA = zkLock.tryLock("clientA");
System.out.println(" 获取结果: " + lockA);
System.out.println("\n2. 客户端B获取锁:");
boolean lockB = zkLock.tryLock("clientB");
System.out.println(" 获取结果: " + lockB);
System.out.println("\n3. 客户端A释放锁:");
zkLock.unlock("clientA");
System.out.println("\n4. 客户端B获取锁:");
boolean lockB2 = zkLock.tryLock("clientB");
System.out.println(" 获取结果: " + lockB2);
zkLock.unlock("clientB");
System.out.println("Zookeeper分布式锁演示完成\n");
}
private void demonstrateDatabaseLock() {
System.out.println("--- 数据库分布式锁演示 ---");
DatabaseDistributedLock dbLock = new DatabaseDistributedLock("resource_lock");
System.out.println("1. 客户端A获取锁:");
boolean lockA = dbLock.tryLock("clientA", 30000);
System.out.println(" 获取结果: " + lockA);
System.out.println("\n2. 客户端B获取锁:");
boolean lockB = dbLock.tryLock("clientB", 30000);
System.out.println(" 获取结果: " + lockB);
System.out.println("\n3. 客户端A释放锁:");
boolean unlockA = dbLock.unlock("clientA");
System.out.println(" 释放结果: " + unlockA);
dbLock.unlock("clientB");
System.out.println("数据库分布式锁演示完成\n");
}
}
// Redis分布式锁
class RedisDistributedLock {
private String lockKey;
private java.util.Map<String, String> lockStorage = new java.util.concurrent.ConcurrentHashMap<>();
private java.util.Map<String, Long> lockExpiry = new java.util.concurrent.ConcurrentHashMap<>();
public RedisDistributedLock(String lockKey) {
this.lockKey = lockKey;
}
public boolean tryLock(String clientId, long expireTime) {
System.out.println(" " + clientId + " 尝试获取Redis锁: " + lockKey);
// 检查锁是否已过期
cleanExpiredLocks();
// 使用SET NX PX命令原子性获取锁
if (!lockStorage.containsKey(lockKey)) {
lockStorage.put(lockKey, clientId);
lockExpiry.put(lockKey, System.currentTimeMillis() + expireTime);
System.out.println(" 获取锁成功");
return true;
} else {
System.out.println(" 锁已被占用: " + lockStorage.get(lockKey));
return false;
}
}
public boolean unlock(String clientId) {
System.out.println(" " + clientId + " 尝试释放Redis锁: " + lockKey);
String lockOwner = lockStorage.get(lockKey);
if (clientId.equals(lockOwner)) {
lockStorage.remove(lockKey);
lockExpiry.remove(lockKey);
System.out.println(" 释放锁成功");
return true;
} else {
System.out.println(" 释放锁失败: 不是锁的持有者");
return false;
}
}
private void cleanExpiredLocks() {
long currentTime = System.currentTimeMillis();
lockExpiry.entrySet().removeIf(entry -> {
if (entry.getValue() < currentTime) {
lockStorage.remove(entry.getKey());
return true;
}
return false;
});
}
}
// Zookeeper分布式锁
class ZookeeperDistributedLock {
private String lockPath;
private java.util.Map<String, String> lockNodes = new java.util.concurrent.ConcurrentHashMap<>();
private java.util.concurrent.atomic.AtomicInteger sequenceNumber = new java.util.concurrent.atomic.AtomicInteger(0);
public ZookeeperDistributedLock(String lockPath) {
this.lockPath = lockPath;
}
public boolean tryLock(String clientId) {
System.out.println(" " + clientId + " 尝试获取ZK锁: " + lockPath);
// 创建临时顺序节点
String nodePath = lockPath + "/" + clientId + "-" + sequenceNumber.incrementAndGet();
lockNodes.put(clientId, nodePath);
// 检查是否是最小节点
if (isSmallestNode(nodePath)) {
System.out.println(" 获取锁成功,节点: " + nodePath);
return true;
} else {
System.out.println(" 获取锁失败,等待前序节点");
return false;
}
}
public void unlock(String clientId) {
System.out.println(" " + clientId + " 释放ZK锁");
String nodePath = lockNodes.remove(clientId);
if (nodePath != null) {
System.out.println(" 删除节点: " + nodePath);
System.out.println(" 释放锁成功");
}
}
private boolean isSmallestNode(String nodePath)
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
Java面试圣经 文章被收录于专栏
Java面试圣经,带你练透java圣经
