redis怎么解决分布式锁

setnx 如果当前有这个key就返回0没有就设置成功返回1

expire给指定key设置过期时间,如果不设置过期时间的话,当执行完一遍的时候,别的线程就永远进不来了.

客户端代码实现,但是这样的话还是会有问题,如果执行完设置key后代码宕掉了,超时时间没设置上,那么这个锁就会一直锁的

 

时间单位有两种  ex为秒  px 为毫秒 就是下图中ex的位置

nx的位置可以填两种  ex为key存不存在时才能进行操作,存在的时候返回nul   xx为key存在时才能进行操作,不存在时返回nil   成功时返回ok

这种方式就是把设置超时时间和设置key合并到一块执行了,就不存在上面那种问题了

 

实现代码

    public void testRedisLockDemo() throws Exception{
        /**
         * 1.设置key的时候得加上过期时间,因为 如果不设置过期时间的话,当key设置成功,但是下面的delete操作因为异常等原因没有执行
         * 这样的话就成了死锁了
         * 2.设置uuid的原因是  假设业务逻辑代码执行时间过长,失效时间过了然后当前线程逻辑才执行完,这时别的线程已经拿到了锁,但是当前线程
         * 如果把lock删掉的话,这个分布式锁就锁不住了,所以设置一个uuid,只让当前线程删自己的锁.
         *
         */
        String uuid = UUID.randomUUID().toString();
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);

        if (lock){
            //设置成功 说明抢到了锁


            //开始时间毫秒值 用来实现续过期时间
            long startTime = System.currentTimeMillis();

            /**
             * 解决办法1:把过期时间设置的特别长
             * 解决办法2:自己实现一个续过期时间的操作
             */
            Thread thread = new Thread(() -> {
                while (true) {
                    //获取key剩余时间
                    Long expire = stringRedisTemplate.boundHashOps("lock").getExpire();

                    //如果剩余时间小于3秒 ,再给当前key设置10秒
                    if (expire < 3) {
                        stringRedisTemplate.expire("lock",10 , TimeUnit.SECONDS);
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            
            System.out.println("执行业务代码");
            Thread.sleep(5000);

            //当执行完成后停掉上面的线程
            thread.stop();


            //释放锁资源
            String lock1 = stringRedisTemplate.opsForValue().get("lock");
            if (uuid.equals(lock1)){
                //删除时为什么不使用简单删除,而是使用脚本删除
                /**
                 * 假设我逻辑代码执行了九秒,lock的超时时间为10秒,我从redis取数据的时间为1.5秒,前半秒去redis取到了数据
                 * 并且返回到程序当中,但是现在在返回回来的时候那个lock已经自动删除了,现在新的lock是别的线程创建的
                 * 但是当前线程执行删除操作把它删掉了,这样就又锁不住了,执行这个脚本的就可以保证这个原子性.
                 */
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";
                //stringRedisTemplate.delete("lock");
                stringRedisTemplate.execute(new DefaultRedisScript<Long>(script,Long.class), Arrays.asList("lock"),uuid);
            }
        }else{
            //没有抢到,继续循环获取
            Thread.sleep(300); //设置下缓冲时间
            testRedisLockDemo();
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值