分布式锁从0到1落地实现02(mysql/redis/zk)

1 redis 实现分布式锁

redis 实现分布式锁应该注意的问题
    //1:独占排他预防栈内存溢出
    //2:预防死锁 集群中客户端的某个节点挂了,自己的锁没释放,导致其他客户端没法获取到锁
    //3:原子性
    //4:防止误删
    //5:可重入
    //6:自动续期
    第一个案例:
     public void reduce() {
        //1:独占排他  使用setnx来实现
        //没有过期时间当集群中的某个客户端掉线 ,并且还没执行finally 中的 删除操作没办法释放锁
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "1");
        if(!lock){
            try {
                Thread.sleep(50);
                reduce();  //递归的方式可能会导致栈内存溢出
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }else{
            try{
                String stock = this.redisTemplate.opsForValue().get("stock");
                if (StringUtils.isNoneBlank(stock) && Integer.parseInt(stock) != 0) {
                    Integer st = Integer.parseInt(stock);
                    if (st > 0) {
                        this.redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                    }
                }
            }catch (Exception e){

            }finally {
                redisTemplate.delete("lock");
            }
        }
    }
    
    第二个案例 解决客户端下线没办法解锁的问题以及栈内存溢出问题
        public void reduce() {
        //1:独占排他  使用setnx来实现ex 来解决客户端下线无法释放锁的问题
        //2:使用循环代替递归,避免栈内存溢出的问题
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3,TimeUnit.SECONDS);
        while(!lock){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
            try{
               // this.redisTemplate.expire("stock",3, TimeUnit.SECONDS); //不具备原子性
                String stock = this.redisTemplate.opsForValue().get("stock");
                if (StringUtils.isNoneBlank(stock) && Integer.parseInt(stock) != 0) {
                    Integer st = Integer.parseInt(stock);
                    if (st > 0) {
                        this.redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                    }
                }
            }catch (Exception e){

            }finally {
                //当超过过期时间我们的主流程还没执行完成,这时候 锁过期了 ,但是另外一个线程进来之后发现没有锁了
                //于是第二个线程加了锁,这时候 第一个线程的业务执行完了要释放锁,就会把第二个线程的锁 ,这时候锁就失效了
                //出现了误删
                //所以这里需要处理两个问题 防止误删
                //还需要自动续期来解决我们的业务逻辑在过期时间内没执行完的情况

                //这种写法 看上去解决了防止误删的问题,但是其实因为查询和删除操作不是原子性的 也还会出问题所以还要借助于LUA 脚本来处理

                if(StringUtils.equals(redisTemplate.opsForValue().get("lock"),uuid)){
                    redisTemplate.delete("lock");
                }
            }
    }

==================================================================
    public void reduce() {
        //1:独占排他  使用setnx来实现ex 来解决客户端下线无法释放锁的问题
        //2:使用循环代替递归,避免栈内存溢出的问题
        String uuid = UUID.randomUUID().toString();
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid,3,TimeUnit.SECONDS);
        while(!lock){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        try{
            // this.redisTemplate.expire("stock",3, TimeUnit.SECONDS); //不具备原子性
            String stock = this.redisTemplate.opsForValue().get("stock");
            if (stock!= null && stock.length() != 0) {
                Integer st = Integer.parseInt(stock);
                if (st > 0) {
                    this.redisTemplate.opsForValue().set("stock", String.valueOf(--st));
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //解决误删 和 操作原子性问题
            //set lock 111
            // eval "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end" 1 lock 111

            String script ="if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
            redisTemplate.execute(new DefaultRedisScript<>(script,Boolean.class), Arrays.asList("lock"),uuid);
//            if(StringUtils.equals(redisTemplate.opsForValue().get("lock"),uuid)){
//                    redisTemplate.delete("lock");
//             }
        }
    }

====================================================
可重入锁使用 redis  hash 的存储结构
hset  key field value 
key  lockName
filed uuid:thread号
value 没重入一次 加1  每释放一次锁 减去1

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值