zoukankan      html  css  js  c++  java
  • redis分布式锁的使用

    1.通过官方文档查看相关的命令操作:http://www.redis.cn/commands.html

      关键命令:setIfAbsent(String key,String value)对应官方文档的 setnx

           解释:将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。

          getAndSet

          解释:自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。(get旧值,set新值)

    2.编写redisLock,即上锁和解锁的方法:

    @Service
    @Slf4j
    public class RedisLock {
        @Autowired
        StringRedisTemplate template;
    
        public boolean lock(String key,String value){
            //1.如果能设置value表示锁没被占用,返回true表示上锁成功,否则进入下一步
            if (template.opsForValue().setIfAbsent(key,value)) {
                return true;
            }
            
            String currentValue = template.opsForValue().get(key);
            //2.这一步是为了防止死锁,因为如果被上锁的那段程序抛出异常,会导致无法执行解锁方法从而导致死锁
            //所以value的值应设为当前时间+超时时间,当Redis里的currentValue小于线程当前的系统时间,
            //说明死锁产生了,需解开这个死锁
            if(!StringUtils.isEmpty(currentValue)&&Long.valueOf(currentValue)<System.currentTimeMillis()) {
                //3.解死锁的操作,把原来的currentValue设置为新的value,并为拿到锁的新线程重新上锁
                String oldValue = template.opsForValue().getAndSet(key, value);
                //4.这步是为了防止在死锁产生的时候,同时有多个线程进来上锁
                // ,只有oldValue等于currentValue的线程才可成功上锁
                if(!StringUtils.isEmpty(oldValue)&&oldValue.equals(currentValue)) {
                    template.opsForValue().set(key,value);
                    return true;
                }
            }
            //上锁失败
            return false;
        }
    
        public void unlock(String key,String value){
            
            try {
                String currentValue = template.opsForValue().get(key);
                //只有在Redis里面的CurrentValue等于传进来的Value才能解锁
                if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                    template.delete(key);
                }
            }catch (Exception e){
                log.error("【redis分布式锁】解锁异常, {}", e);
            }
        }
    }

    3.在相应的程序里调用redisLock

     @Override
        public String orderSecKillProduct(String productId) {
            //value的值设为当前时间+超时时间
            String time=String.valueOf(System.currentTimeMillis()+10*1000);
            //加锁
            if(!redisLock.lock(productId,time)){
                //上锁失败,抛出异常跳转到相应的页码
                throw  new SellException(101,"太多人抢购了,请重试");
            }
            //查询库存若为0则,提示已抢购完
            Integer stockNum = productStock.get(productId);
            if(stockNum==0){
                throw new SellException(100,"已被抢购完了");
            }
            //下单
            ordered.put(KeyUtil.genUniqueKey(), productId);
            //减库存
            stockNum=stockNum-1;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //存入数据库
            productStock.put(productId,stockNum);
            //解锁
            redisLock.unlock(productId,time);
            //查询
            return query(productId);
        }
    setIfAbsent
  • 相关阅读:
    visual studio项目多级引用不拷贝dll的问题
    ef6 code first,对已有数据库如何执行迁移
    wsl 修改默认安装路径
    Windows docker镜像文件无法删除
    Docker镜像下载很慢,各种加速无效
    activemq整合springboot使用(个人微信小程序用)
    angular入门一之环境安装及项目创建
    jquery中attr()和prop()的区别
    IntelliJ IDEA部署web项目,Tomcat没有出现Artifacts
    IntelliJ IDEA:创建Java Web工程
  • 原文地址:https://www.cnblogs.com/shouyaya/p/13258414.html
Copyright © 2011-2022 走看看