18.7.5 Redis分布式锁实现与优化

1. Redis分布式锁基础实现

1.1 基础分布式锁

public class RedisDistributedLock {
    
    /*
     * Redis分布式锁基础实现:
     * 
     * 1. 核心命令
     *    - SET key value NX PX milliseconds
     *    - NX: 只在键不存在时设置
     *    - PX: 设置过期时间(毫秒)
     * 
     * 2. 基本流程
     *    - 获取锁:SET操作成功
     *    - 释放锁:DEL操作删除
     *    - 超时释放:自动过期
     * 
     * 3. 问题与改进
     *    - 锁误删问题
     *    - 锁续期问题
     *    - 可重入问题
     */
    
    public void demonstrateBasicLock() {
        System.out.println("=== Redis分布式锁基础实现演示 ===");
        
        MockRedisClient redis = new MockRedisClient();
        BasicDistributedLock lock = new BasicDistributedLock(redis);
        
        // 演示基础锁操作
        demonstrateLockOperations(lock);
        
        // 演示并发场景
        demonstrateConcurrentLock(lock);
    }
    
    private void demonstrateLockOperations(BasicDistributedLock lock) {
        System.out.println("--- 基础锁操作 ---");
        
        String lockKey = "order:lock:1001";
        String requestId = "req-" + System.currentTimeMillis();
        
        System.out.println("1. 尝试获取锁:");
        boolean acquired = lock.tryLock(lockKey, requestId, 30000);
        System.out.println("获取锁结果: " + acquired);
        
        if (acquired) {
            System.out.println("\n2. 执行业务逻辑:");
            System.out.println("处理订单业务...");
            
            try {
                Thread.sleep(1000); // 模拟业务处理时间
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            
            System.out.println("\n3. 释放锁:");
            boolean released = lock.unlock(lockKey, requestId);
            System.out.println("释放锁结果: " + released);
        }
    }
    
    private void demonstrateConcurrentLock(BasicDistributedLock lock) {
        System.out.println("\n--- 并发锁测试 ---");
        
        String lockKey = "concurrent:lock:test";
        
        // 启动多个线程竞争锁
        for (int i = 1; i <= 5; i++) {
            final int threadId = i;
            new Thread(() -> {
                String requestId = "thread-" + threadId;
                boolean acquired = lock.tryLock(lockKey, requestId, 10000);
                
                if (acquired) {
                    System.out.println("线程" + threadId + " 获取锁成功");
                    try {
                        Thread.sleep(2000); // 模拟业务处理
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    lock.unlock(lockKey, requestId);
                    System.out.println("线程" + threadId + " 释放锁");
                } else {
                    System.out.println("线程" + threadId + " 获取锁失败");
                }
            }).start();
        }
    }
}

// 模拟Redis客户端
class MockRedisClient {
    private java.util.Map<String, String> data = new java.util.concurrent.ConcurrentHashMap<>();
    private java.util.Map<String, Long> expireTime = new java.util.concurrent.ConcurrentHashMap<>();
    
    public boolean setNX(String key, String value, long expireMs) {
        // 检查过期
        if (expireTime.containsKey(key) && System.currentTimeMillis() > expireTime.get(key)) {
            data.remove(key);
            expireTime.remove(key);
        }
        
        // 尝试设置
        if (!data.containsKey(key)) {
            data.put(key, value);
            expireTime.put(key, System.currentTimeMillis() + expireMs);
            System.out.println("  Redis SET " + key + " " + value + " NX PX " + expireMs + " -> OK");
            return true;
        } else {
            System.out.println("  Redis SET " + key + " " + value + " NX PX " + expireMs + " -> NIL");
            return false;
        }
    }
    
    public String get(String key) {
        // 检查过期
        if (expireTime.containsKey(key) && System.currentTimeMillis() > expireTime.get(key)) {
            data.remove(key);
            expireTime.remove(key);
            return null;
        }
        return data.get(key);
    }
    
    public boolean delete(String key, String expectedValue) {
        String currentValue = get(key);
        if (expectedValue.equals(currentValue)) {
            data.remove(key);
            expireTime.remove(key);
            System.out.println("  Redis DEL " + key + " -> 1");
            return true;
        } else {
            System.out.println("  Redis DEL " + key + " -> 0 (value mismatch)");
            return false;
        }
    }
    
    public Object eval(String script, java.util.List<String> keys, java.util.List<String> args) {
        System.out.println("  Redis EVAL script with keys: " + keys + ", args: " + args);
        
        // 模拟Lua脚本执行
        if (script.contains("redis.call('get', KEYS[1])")) {
            String key = keys.get(0);
            String expectedValue = args.get(0);
            String currentValue = get(key);
            
            if (expectedValue.equals(currentValue)) {
                data.remove(key);
                expireTime.remove(key);
                return 1L;
            } else {
                return 0L;
            }
        }
        
        return 0L;
    }
}

// 基础分布式锁实现
class BasicDistributedLock {
    private MockRedisClient redis;
    
    public BasicDistributedLock(MockRedisClient redis) {
        this.redis = redis;
    }
    
    public boolean tryLock(String lockKey, String requestId, long expireMs) {
        return redis.setNX(lockKey, requestId, expireMs);
    }
    
    public boolean unlock(String lockKey, String requestId) {
        // 使用Lua脚本保证原子性
        String script = 
            "if redis.call('get', KEYS[1]) == ARGV[1] then " +
            "    return redis.call('del', KEYS[1]) " +
            "else " +
            "    return 0 " +
            "end";
        
        Object result = redis.eval(script, 
            java.util.Arrays.asList(lockKey), 
            java.util.Arrays.asList(requestId));
        
        return Long.valueOf(1).equals(result);
    }
}

2. 高级分布式锁实现

2.1 可重入分布式锁

public class ReentrantDistributedLock {
    
    /*
     * 可重入分布式锁:
     * 
     * 1. 可重入特性
     *    - 同一线程可多次获取锁
     *    - 记录重入次数
     *    - 释放时递减计数
     * 
     * 2. 实现方案
     *    - 使用Hash结构存储
     *    - field: 线程标识
     *    - value: 重入次数
     * 
     * 3. 锁续期机制
     *    - 看门狗自动续期
     *    - 避免业务执行时间过长导致锁过期
     */
    
    public void demonstrateReentrantLock() {
        System.out.println("=== 可重入分布式锁演示 ===");
        
        MockRedisClient redis = new MockRedisClient();
        AdvancedDistributedLock lock = new AdvancedDistributedLock(redis);
        
        demonstrateReentrantFeature(lock);
        demonstrateWatchdog(lock);
    }
    
    private void demonstrateReentrantFeature(AdvancedDistributedLock lock) {
        System.out.println("--- 可重入特性演示 ---");
        
        String lockKey = "reentrant:lock:test";
        String threadId = Thread.currentThread().getName();
        
        System.out.println("1. 第一次获取锁:");
        boolean acquired1 = lock.lock(lockKey, threadId, 30000);
        System.out.println("获取结果: " + acquired1);
        
        System.out.println("\n2. 第二次获取锁 (重入):");
        boolean acquired2 = lock.lock(lockKey, threadId, 30000);
        System.out.println("获取结果: " + acquired2);
        
        System.out.println("\n3. 第三次获取锁 (重入):");
        boolean acquired3 = lock.lock(lockKey, threadId, 30000);
        System.out.println("获取结果: " + acquired3);
        
        System.out.println("\n4. 释放锁:");
        lock.unlock(lockKey, threadId);
        System.out.println("5. 再次释放锁:");
        lock.unlock(lockKey, threadId);
        System.out.println("6. 最后释放锁:");
        lock.unlock(lockKey, threadId);
    }
    
    private void demonstrateWatchdog(AdvancedDistributedLock lock) {
        System.out.println("\n--- 看门狗续期演示 ---");
        
        String lockKey = "watchdog:lock:test

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

Java面试圣经 文章被收录于专栏

Java面试圣经,带你练透java圣经

全部评论

相关推荐

评论
点赞
1
分享

创作者周榜

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