Redis分布式锁

自己的学习笔记,如有错误,请您指导!

1.1直接设置锁(若在释放锁之前抛异常 则这个锁就不会被释放)
    public void testLock(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
        //先判断 如果key不存在 才设置成功 相当于上锁
        Boolean isLock = valueOperations.setIfAbsent("k1", "v1");
        if (isLock){
            valueOperations.set("name","xxxx");
            String name = (String) valueOperations.get("name");
            System.out.println("name = " + name);
            //模拟异常
            int i = 10/0;
            //操作结束 删除锁
            //但是若在释放锁之前抛异常 则这个锁就不会被释放 第二个线程 进来还是获取不到锁
            redisTemplate.delete("k1");
        }else {
            System.out.println("有线程在使用,请稍后再试");
        }
    }

1.2设置有时效的锁(抛异常之后锁也会释放)
public void testLock1(){
    ValueOperations valueOperations = redisTemplate.opsForValue();
    Boolean isLock = valueOperations.setIfAbsent("k1", "v1", 5, TimeUnit.SECONDS);
    if (isLock){
        valueOperations.set("name","xxxx");
        String name = (String) valueOperations.get("name");
        System.out.println("name = " + name);
        //模拟异常
        int i = 10/0;
        //操作结束 删除锁
        //因为抛异常了 所以不会走到这一步,但是前面上锁的时候设置了时间,5秒之后 就可以获取该锁了
        redisTemplate.delete("k1");
    }
}
1.3设置有时效的锁 (出现的问题)
public void testLock1(){
    ValueOperations valueOperations = redisTemplate.opsForValue();
    Boolean isLock = valueOperations.setIfAbsent("k1", "v1", 5, TimeUnit.SECONDS);
    if (isLock){
        //模拟做自己的业务耗时7秒
          try{ 
              TimeUnit.SECONDS.sleep(7); 
          } catch (InterruptedException e) {
              e.printStackTrace(); 
          }
        redisTemplate.delete("k1");
    }
}

但是会有问题 ,假设A线程获取到锁,但是它执行耗时了7秒,所以他还没执行完的时候就已经所就已经被释放了(然后B线程获取该锁),那么当他执行完以后执行redisTemplate.delete(“k1”) 释放的是B线程的锁。假设线程多了以后并且执行业务时间也更长 ,那么就会更乱。

1.4使用lua脚本可以原子性的执行多条redis指令

为了解决上面的问题,可以在删除锁的时候判断这个锁是不是自己的锁,所以在设置锁定时候使用UUID生成一个随机的串当作自己的锁的value。

但是在释放锁的过程中需要先获取锁,然后判断是不是自己的锁,最后释放锁,我们需要原子性的执行这三条操作,所以使用lua脚本。

1.在resource下创建lock.lua

if redis.call("get",KEYS[1])==ARGV[1] then
   return redis.call("del",KEYS[1])
else
   return 0
end

脚本解释:redis get到KEYS集合(后面调用传进来的)里key的value 然后和ARGV集合(后面调用传进来的)里的value比对。如果一样就执行redis.call(“del”,KEYS[1]),也就是执行del 对KEYS集合里的key删除。

2.在RedisConfig中配置(配置类 注意加上@Configuration)

@Configuration
public class RedisConfig {
    @Bean
    public DefaultRedisScript<Boolean> script(){
        DefaultRedisScript<Boolean> redisScript = new DefaultRedisScript<>();
        //lock.lua 脚本位置和application.yml同级目录
        redisScript.setLocation(new ClassPathResource("lock.lua"));
        redisScript.setResultType(Boolean.class);
        return redisScript;
    }

3.使用lua脚本的锁

 public void testLock2(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
     //给k1设置随机值作为唯一标识
        String value = UUID.randomUUID().toString();
        Boolean isLock = valueOperations.setIfAbsent("k1", value,5,TimeUnit.SECONDS);
        if (isLock){
            valueOperations.set("name","xxxx");
            String name = (String) valueOperations.get("name");
            System.out.println("name = " + name);
            System.out.println(valueOperations.get("k1"));
             //在脚本中进行比对  Collections.singletonList("k1")就是KEYS[1]  value就是ARGV[1]
            Boolean result = (Boolean) redisTemplate.execute(script, Collections.singletonList("k1"), value);
            System.out.println(result);
        }else {
            System.out.println("有线程在使用,请稍后再试");
        }
    }

4.使用redis的事务

public void testLock2(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
     //给k1设置随机值作为唯一标识
        String value = UUID.randomUUID().toString();
        Boolean isLock = valueOperations.setIfAbsent("k1", value,5,TimeUnit.SECONDS);
        if (isLock){
            valueOperations.set("name","xxxx");
            String name = (String) valueOperations.get("name");
            System.out.println("name = " + name);
            System.out.println(valueOperations.get("k1"));
            
            while (true){
                redisTemplate.watch("k1");
                if (valueOperations.get("k1").equals(value)){
                    redisTemplate.setEnableTransactionSupport(true);
                    redisTemplate.multi();
                    redisTemplate.delete("k1");
                    List list = redisTemplate.exec();
                    if (list == null){//说明在监视期间被动过
                        continue;
                    }
                }
                redisTemplate.unwatch();
                break;
            }
        }else {
            System.out.println("有线程在使用,请稍后再试");
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值