• 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秒),就继续延长锁的时间,如果当前线程挂了,那么就不会自动延长锁,到期之后,锁会自动释放

  • 相关阅读:
    IOS-多线程技术
    设计模式-抽象工厂设计模式
    IOS-内存管理
    IOS-MVC的使用
    POJ2411 Mondriaan's Dream (广场铺砖问题 状压dp)
    NOIp2006T2 金明的预算方案
    POJ1179 Polygon(区间DP)
    NOIp2006T1能量项链
    美梦1(JSOI2014SC)
    TJOI2013(BZOJ3173)最长上升子序列
  • 原文地址:https://www.cnblogs.com/shuxiaosheng/p/14990428.html
Copyright © 2020-2023  润新知