Redis分布式锁

Redis分布式锁

1. 引入jar包

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

2. 分布式锁实现

  • setnx命令

    set if not exist,顾名思义,当不存在key的时候,则创建key并赋值为value。

    分布式架构中,每当一个线程进入方法,则调用redis的setnx的方法:

    如果赋值成功,则说明当前没有对应的key,也就是当前没有其他线程操作该方法,获取到锁,执行逻辑,最后释放锁。

    如果没有赋值成功,则说明当前key已存在,即已经有线程在操作该方法,需要等待线程释放锁。

  • 代码实现

        @Autowired
        RedisTemplate redisTemplate;
    
    public void testLock(){
    	String lock = "cz";
              //如果没有这个key,则赋值,返回成功。
           Boolean absent =
               redisTemplate.opsForValue().setIfAbsent(lock, "lock");
          
                if(absent){
                   //如果拿到锁,可以执行自己的业务逻辑
                }else{
                    //没拿到,可以给出提示或者一些娄底的措施。
                }
        		//释放锁
                redisTemplate.delete(lock);
        }
    

    以上写法有没有问题?虽然实现了锁功能,但是,如果程序运行中,代码耗时长,或者因为异常,导致一直没有执行释放锁的操作,则会导致其他线程一直等待状态;另外,如果第一次获取锁失败了,是否可以做一些重试机制?

  • 锁优化–设置超时与重试

    public void testLock(){
         String lock = "cz";
         Integer i = 0;
         while (true && i<2){
           Boolean absent =
               redisTemplate.opsForValue().setIfAbsent(lock, "lock");
                if(absent){
                    //如果复制成功了,说明拿到了锁,设置key的有效期
                    redisTemplate.expire(lock,10, TimeUnit.SECONDS);
                }else {
                    //拿不到锁,歇100毫秒重试一次
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    i++;
                }
            }
                redisTemplate.delete(lock);
            
        }
    

    上述代码,已经基本解决了异常超时问题,并且添加锁机制。但是,仔细分析,是否存在这么一个情况,x线程A进入方法,设置了key有效期为10s,但是实际由于业务逻辑复杂,代码执行时间超过了10s,redis还未删除key的时候,key已经先行失效。B进入方法,发现没有key,可以赋值,也可以拿到锁,正准备执行逻辑时,A线程代码执行结束,准备执行delete()方法删除key,但是A的key早就失效,删除了线程B生成的key。。。
    在这里插入图片描述

要想解决上述问题的关键点,就是只能删除自己所持有的key。所以,再执行删除key的时候,需要判断value是不是自己设置的value。

  • 优化

        }
    
        public void testLock(){
            String lock = "cz";
            Integer i = 0;
            while (true && i<2){
                //如果没有这个key,则赋值,返回成功。
       			Boolean absent = redisTemplate.opsForValue().setIfAbsent(lock, "lock");//此处value要设置唯一值
                if(absent){
                    //如果复制成功了,说明拿到了锁,设置key的有效期
                    redisTemplate.expire(lock,10, TimeUnit.SECONDS);
                }else {
                    //拿不到锁,歇100毫秒重试一次
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    i++;
                }
            }
            String lockStr = redisTemplate.opsForValue().get(lock).toString();
            if(!StringUtils.isEmpty(lockStr) && lockStr.equals("lock")){
                //如果获取到的value不为空,并且与自己之前设置的value一致,则删除key。
                redisTemplate.delete(lock);
            }
        }
    

    扩展

    SpringBoot敏捷开发的重要原因之一是无需过多配置,可自动装配。集成Redis后,可以通过读取spring.factories中的配置类信息,直线自动配置。

1.spring-boot-autoconfigure:250
在这里插入图片描述
2. 找到redis相关的配置类
在这里插入图片描述
3.redis配置在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值