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圣经

全部评论

相关推荐

点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务