Redis分布式锁

redis1.0版本的锁(有bug版本的分布式锁)
    @GetMapping("/Redis1/{id}")
    public String RedisLock1(@PathVariable("id") String id){
        String LockName="LockName";
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(LockName, "shuxiaosheng",10,TimeUnit.SECONDS);
        if (lock!=null&&!lock){
            return "系统繁忙,请稍微再试";
        }
        try {
            Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock>0){
                Integer realStock=stock-1;
                stringRedisTemplate.opsForValue().set("stock",realStock.toString());
            }
        }finally{
            stringRedisTemplate.delete(LockName);
        }
        return "end";

    }

模拟因处理业务时间过长而导致的分布式锁失效的情况

    @GetMapping("/Redis1/{id}")
    public String RedisLock1Exception(@PathVariable("id") Integer id) throws Exception{
        String LockName="LockName";
        Boolean shuxiaosheng = stringRedisTemplate.opsForValue().setIfAbsent(LockName, "线程"+id,10,TimeUnit.SECONDS);
        if (shuxiaosheng!=null&&!shuxiaosheng){
            return "系统繁忙,请稍微再试";
        }
        try {
            //线程暂停超过十秒,锁就自动释放了
            if(1==id){
                Thread.sleep(15000);
                System.out.println("线程1暂停15秒,锁已经自动释放");
            }
            if(2==id){
                Thread.sleep(8000);
                System.out.println("线程2暂停8秒");
            }
            Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock>0){
                Integer realStock=stock-1;
                stringRedisTemplate.opsForValue().set("stock",realStock.toString());
            }
        }finally{
            System.out.println("当前线程是:"+id+"释放的锁是:"+stringRedisTemplate.opsForValue().get(LockName));
            stringRedisTemplate.delete(LockName);
        }
        return "end";

    }
1.0版本的存在的缺陷,例如:
  1. 当线程1进来,执行业务代码需要15秒.
  2. 当线程2进来,执行业务代码需要8秒.
  3. 线程1最先进来,拿到了锁,当线程1执行业务代码到11秒时,锁被自动释放了.
  4. 线程2立马进来,拿到了锁,当线程2执行业务代码到4秒时,这时,线程1业务代码已经执行结束,并且手动释放了线程2加的锁,
控制台打印
线程1暂停15秒,锁已经自动释放
当前线程是:1释放的锁是:线程2
线程2暂停8秒
当前线程是:2释放的锁是:null
总结:

因为高并发,线程1执行业务的代码时间大于锁超时时间,线程1的锁自动释放了,线程2加入进来,结果线程1执行完毕,释放了线程2加的锁,出现问题

redis2.0版本的锁(适用于并发一般的软件公司)
    @GetMapping("/Redis2/{id}")
    public String RedisLock12(@PathVariable("id") Integer id) throws Exception{
        String LockName="LockName";
        String uuid = UUID.randomUUID().toString();
        Boolean shuxiaosheng = stringRedisTemplate.opsForValue().setIfAbsent(LockName, uuid+"线程"+id,10,TimeUnit.SECONDS);
        if (shuxiaosheng!=null&&!shuxiaosheng){
            return "系统繁忙,请稍微再试";
        }
        try {
            //线程暂停十秒,锁就自动释放了
            if(1==id){
                Thread.sleep(15000);
                System.out.println("线程1暂停15秒,锁已经自动释放");
            }
            if(2==id){
                Thread.sleep(8000);
                System.out.println("线程2暂停8秒");
            }
            Integer stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
            if (stock>0){
                Integer realStock=stock-1;
                stringRedisTemplate.opsForValue().set("stock",realStock.toString());
            }
        }finally{
            //当前线程只能释放自己加的锁
            if ((uuid+"线程"+id).equals(stringRedisTemplate.opsForValue().get(LockName))){
                System.out.println("当前线程是:"+id+"释放的锁是:"+stringRedisTemplate.opsForValue().get(LockName));
                stringRedisTemplate.delete(LockName);
            }

        }
        return "end";

    }
控制台打印
线程1暂停15秒,锁已经自动释放
线程2暂停8秒
当前线程是:2释放的锁是:c579e42a-ecfb-40a4-8e5f-689f3d858f40线程2
总结

redis2.0相比较与redis1.0,就加了个核心判断,当前线程只能释放自己加的锁,不能释放别人的锁

到此简单的redis锁就已经实现了

redis3.0版本的锁(适用并发比较高互联网公司)
添加依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.4.1</version>
            <exclusions>
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.13.6</version>
        </dependency>
添加配合类
@Configuration
public class MyRedissonConfig {

    @Bean(destroyMethod="shutdown")
    RedissonClient redisson() throws IOException {
        //1、创建配置
        Config config = new Config();
        config.useSingleServer()
                .setAddress("127.0.0.1:6379");
        return Redisson.create(config);
    }
    
}
实现
 @RequestMapping("/redisson")
    public String testRedisson(){
        //获取分布式锁,只要锁的名字一样,就是同一把锁
        RLock lock = redissonClient.getLock("lock");

        //加锁(阻塞等待),默认过期时间是30秒
        lock.lock();
        try{
            //如果业务执行过长,Redisson会自动给锁续期
            Thread.sleep(1000);
            System.out.println("加锁成功,执行业务逻辑");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            //解锁,如果业务执行完成,就不会继续续期,即使没有手动释放锁,在30秒过后,也会释放锁
            lock.unlock();
        }

        return "Hello Redisson!";
    }
原理

大家都知道,如果刚上完锁,负责存储这个分布式的Redisson节点宕机后,这样就出现了死锁,为了避免这种情况发生,Redisson内部提供了一个监控锁的看门狗,他的作用是:如果当前线程还有持有锁,且锁快要到期了(默认锁是30秒),就继续延长锁的时间,如果当前线程挂了,那么就不会自动延长锁,到期之后,锁会自动释放

原文地址:https://www.cnblogs.com/shuxiaosheng/p/14990428.html