zoukankan      html  css  js  c++  java
  • Redis锁实现防重复提交和并发问题

    @Slf4j
    @Component
    public class RedisLock {
    
        public static final int LOCK_EXPIRE = 5000;
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
    
        /**
         *  分布式锁
         *
         * @param key key值
         * @return 是否获取到
         */
        public boolean lock(String key) {
    
            String lock = key;
            try {
                return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
                    long expireAt = System.currentTimeMillis() + LOCK_EXPIRE;
                    Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
                    if (acquire) {
                        log.info("用户 [{}]加锁成功",key);
    
                        return true;
                    } else {
                        log.info("用户 [{}]加锁失败",key);
    
                        //判断该key上的值是否过期了
                        byte[] value = connection.get(lock.getBytes());
                        if (Objects.nonNull(value) && value.length > 0) {
                            long expireTime = Long.parseLong(new String(value));
                            if (expireTime < System.currentTimeMillis()) {
                                // 如果锁已经过期
                                byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE).getBytes());
                                // 防止死锁
                                return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                            }
                        }
                    }
                    return false;
                });
            } finally {
                RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
            }
        }
    
    
    
        /**
         * 删除锁
         *
         * @param key
         */
        public void delete(String key) {
            try {
                redisTemplate.delete(key);
            } finally {
                RedisConnectionUtils.unbindConnection(redisTemplate.getConnectionFactory());
            }
        }
    
    //实现业务
    
            try{
                // 判断是否获取了锁
                boolean getLock = redisLock(lockKey);
                if(getLock){
                    // 此处可以开始写需要实现的代码
                 
                }
            }catch(Exception e){
          e.printStackTrace();
    }finally { 
    // 判断是否超时了,如果未超时,则释放锁。 超时了,锁有可能被其他线程拿走了,就不做任何操作
    redisLock.delete(String.valueOf(caronwerId));
    }
    }

     知识点: redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。

    设置成功,返回 1 。 设置失败,返回 0 。

    redis> EXISTS job                # job 不存在
    (integer) 0
    
    redis> SETNX job "programmer"    # job 设置成功
    (integer) 1
    
    redis> SETNX job "code-farmer"   # 尝试覆盖 job ,失败
    (integer) 0
    
    redis> GET job                   # 没有被覆盖
    "programmer"
    
    一.redis命令讲解: 
    setnx()命令: 
    setnx的含义就是SET if Not Exists,其主要有两个参数 setnx(key, value)。
    
    该方法是原子的,如果key不存在,则设置当前key成功,返回1;如果当前key已经存在,则设置当前key失败,返回0。
    
    get()命令: 
    get(key) 获取key的值,如果存在,则返回;如果不存在,则返回nil; 
    getset()命令: 
    这个命令主要有两个参数 getset(key, newValue)。该方法是原子的,对key设置newValue这个值,并且返回key原来的旧值。 
    假设key原来是不存在的,那么多次执行这个命令,会出现下边的效果: 
    1. getset(key, “value1”) 返回nil 此时key的值会被设置为value1 
    2. getset(key, “value2”) 返回value1 此时key的值会被设置为value2 
    3. 依次类推! 
    二.具体的使用步骤如下: 
    1. setnx(lockkey, 当前时间+过期超时时间) ,如果返回1,则获取锁成功;如果返回0则没有获取到锁,转向2。 
    2. get(lockkey)获取值oldExpireTime ,并将这个value值与当前的系统时间进行比较,如果小于当前系统时间,则认为这个锁已经超时,可以允许别的请求重新获取,转向3。 
    3. 计算newExpireTime=当前时间+过期超时时间,然后getset(lockkey, newExpireTime) 会返回当前lockkey的值currentExpireTime。 
    4. 判断currentExpireTime与oldExpireTime 是否相等,如果相等,说明当前getset设置成功,获取到了锁。如果不相等,说明这个锁又被别的请求获取走了,那么当前请求可以直接返回失败,或者继续重试。 
    5. 在获取到锁之后,当前线程可以开始自己的业务处理,当处理完毕后,比较自己的处理时间和对于锁设置的超时时间,如果小于锁设置的超时时间,则直接执行delete释放锁;如果大于锁设置的超时时间,则不需要再锁进行处理
    
  • 相关阅读:
    Jessica's Reading Problem POJ
    FatMouse and Cheese HDU
    How many ways HDU
    Humble Numbers HDU
    Doing Homework again
    Stacks of Flapjacks UVA
    Party Games UVA
    24. 两两交换链表中的节点
    面试题 03.04. 化栈为队
    999. 可以被一步捕获的棋子数
  • 原文地址:https://www.cnblogs.com/ampl/p/13609165.html
Copyright © 2011-2022 走看看