栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis分布式锁

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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 script(){
        DefaultRedisscript 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("有线程在使用,请稍后再试");
        }
    }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/272444.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号