缓存-分布式锁-分布式锁原理与使用

怎么来实现这个分布式锁呢?

方案一:

这种设计方案,会出现一个问题:当线程获取到锁,然后执行完业务逻辑,准备去删除锁的时候,突然服务器宕机了,会导致这个锁一直存在,得不到释放,会造成死锁的情况。

解决方案就是:设置一个过期时间,即使服务器宕机不能手动释放,也可以过期自动释放

方案二:

解决了方案一的问题,不过,还会有问题,假如当我们获取到锁之后将要去设置过期时间的时候,这时候服务器宕机了,也会造成死锁情况。

解决方案:保证获取锁和设置过期时间是原子性的,setnx ex命令可以保证原子性

方案三:

这种方案解决了设置锁的原子性,但是在删除锁的时候,是应该直接删除的吗?当我们的业务执行时间很长的时候,这时候假定锁已经过期了,别的线程获得了锁,先前线程执行完业务之后,去删除锁,就会去删除别人的锁

解决方案:在设置锁的时候指定自己的UUID,执行完业务后,获取锁检查是否是自己之前设置的,如果是自己设置的,就删除,否则就跳过,在删除锁的时候也要保证原子性,为什么呢?假如我们获取到这个锁的确是我们自己之前设置的,但是在获取值到删除锁中间还是有一段时间,假如这段时间,锁失效了,别人获取到了锁,这时候我们还是会认为锁是自己的,会导致误删。

方案四:

手动如何实现分布式锁

实现分布式锁的前提一定要保证在获取到锁+过期时间、获取锁+删除锁这两步操作都是原子操作。

上图就是我解决方案的流程图。接下来就看看我代码是怎么实现的吧。

/**
 * 从数据库查询并封装数据::分布式锁
 * @return
 */
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {

    //1、占分布式锁。去redis占坑      设置过期时间必须和加锁是同步的,保证原子性(避免死锁)
    String uuid = UUID.randomUUID().toString();
    Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
    if (lock) {
        System.out.println("获取分布式锁成功...");
        Map<String, List<Catelog2Vo>> dataFromDb = null;
        try {
            //加锁成功...执行业务(只允许获取到分布式锁的线程去数据库中查)
            dataFromDb = getDataFromDb();
        } finally {
            // Lua脚本,在脚本中有两步操作:一、获取当前这个分布式锁,判断这个分布式锁是不是我的,二、如果是我的就删除,并返回1,否则返回0
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

            //删除锁
            stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);

        }
        //先去redis查询下保证当前的锁是自己的
        //获取值对比,对比成功删除=原子性 lua脚本解锁
        // String lockValue = stringRedisTemplate.opsForValue().get("lock");
        // if (uuid.equals(lockValue)) {
        //     //删除我自己的锁
        //     stringRedisTemplate.delete("lock");
        // }

        return dataFromDb;
    } else {
        System.out.println("获取分布式锁失败...等待重试...");
        //加锁失败...重试机制
        //休眠一百毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getCatalogJsonFromDbWithRedisLock();     //自旋的方式
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值