分布式锁(设置锁和过期时间)

问题描述:

随着业务发展的需要,原单体单机部署的系统被演化分成分布式集群系统后,由于分布式系统多线程、多进程且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问,这就是分布式锁要解决的问题

分布式锁主流的实现方案:

1、基于数据库实现分布式锁

2、基于缓存(Redis等)

3、基于Zookeeper

每一种分布式锁解决方案都有各自的优缺点。

解决方案:基于Redis实现分布式锁

redis:命令

# set sku:1:info "OK" NX PX 10000

EX second:设置键的过期时间为seconds秒,SET key value EX second效果等同于SETEX key second value

NX::只在键不存在时,才对键进行设置操作,SET key value NX效果等同于SETNX key value

XX:只在键已经存在时,才对键进行设置操作

DEL:释放锁

设置过期时间解决锁一直不能释放的问题

上锁之后出现异常无法设置过期时间:上锁时同时设置过期时间 set users 10 nx ex 10   既上锁又设置过期时间同步进行。

 @GetMapping("/testLock")
    public void testLock(){
        //1、获取锁, setnx
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111", 3, TimeUnit.SECONDS);
        //2、获取锁成功,查询num的值
        if(lock){
            Object value = redisTemplate.opsForValue().get("num");
            //2.1 判断num为空return
            if(StringUtils.isEmpty(value)){
                return;
            }
            //2.2 有值就转成int
            int num = Integer.parseInt(value + "");
            //2。3 把redis的num加1
            redisTemplate.opsForValue().set("num", ++num);
            //2.4 释放锁,del
            redisTemplate.delete("lock");
        }else {
            //3 获取锁失败,每个0.1秒再获取
            try{
                Thread.sleep(100);
                testLock();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }

存在问题:误删,锁释放错了

a:先操作

1、上锁

2、具体操作

服务器卡顿

3、锁自动释放

b:抢到锁,具体操作

a服务器反应过来,进行操作,手动释放锁,释放了b的锁

方法:使用UUID表示不同操作

1、set lock uuid nx ex 10

2、释放锁时,首先判断当前uuid和要释放锁的uuid是否一样。

 @GetMapping("/testLock")
    public void testLock(){
        String uuid = UUID.randomUUID().toString();
        //1、获取锁, setnx
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
        //2、获取锁成功,查询num的值
        if(lock){
            Object value = redisTemplate.opsForValue().get("num");
            //2.1 判断num为空return
            if(StringUtils.isEmpty(value)){
                return;
            }
            //2.2 有值就转成int
            int num = Integer.parseInt(value + "");
            //2。3 把redis的num加1
            redisTemplate.opsForValue().set("num", ++num);
            //2.4 释放锁,del、判断比较uuid是否一样
            if(redisTemplate.opsForValue().get("lock").equals(uuid)){
                redisTemplate.delete("lock");
            }
            
        }else {
            //3 获取锁失败,每个0.1秒再获取
            try{
                Thread.sleep(100);
                testLock();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }

 

posted @ 2023-06-05 21:49  佛系粥米  阅读(336)  评论(0编辑  收藏  举报