• 技术文章 >数据库 >Redis

    redis会发生死锁问题吗

    尚2019-07-08 09:14:05原创3514

    就分布式锁而言,一个常用的问题就是如果一个服务setnx成功了,但是在解锁的时候如果发生了宕机或者一些特殊因素,导致无法解锁,那么其他服务将陷入死锁的状态。所以,我们在用 setnx 的同时想着去用 expire 指令对锁进行一个过期操作, 从指令可以看出 setnx 和expire指令是分开的,如果在这中间的空隙过程中如果有特殊因素导致指令无法继续,也会导致死锁的产生。

    解决方法:

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    import org.springframework.util.StringUtils;
     
    @Component
    public class RedisLock {
     
        Logger logger = LoggerFactory.getLogger(this.getClass());
     
        @Autowired
        private StringRedisTemplate redisTemplate;
     
        /**
         * 加锁
         * @param key   
         * @param value 当前时间 + 超时时间
         * @return
         */
        public boolean lock(String key, String value) {
        
            if (redisTemplate.opsForValue().setIfAbsent(key, value)) {     
                // 这个其实就是setnx命令,只不过在java这边稍有变化,返回的是boolean
                // 设置个过期时间,当然如果在这中间的空隙过程中如果有特殊因素导致指令无法继续,也会导致死锁的产生,如果死锁出现,则后续代码会处理
                redisTemplate.expire(key, lockTime, TimeUnit.SECONDS);
                return true;
            }
     
            // 避免死锁,且只让一个线程拿到锁
            String currentValue = redisTemplate.opsForValue().get(key);
            // 如果锁过期了
            if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
                //获取上一个锁的时间
                String oldValues = redisTemplate.opsForValue().getAndSet(key, value);
     
                /*
                   只会让一个线程拿到锁
                   如果旧的value和currentValue相等,只会有一个线程达成条件,因为第二个线程拿到的oldValue已经和currentValue不一样了
                 */
                if (!StringUtils.isEmpty(oldValues) && oldValues.equals(currentValue)) {
                    return true;
                }
            }
            return false;
        }
     
     
        /**
         * 解锁
         * @param key
         * @param value
         */
        public void unlock(String key, String value) {
            try {
                String currentValue = redisTemplate.opsForValue().get(key);
                if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
                    redisTemplate.opsForValue().getOperations().delete(key);
                }
            } catch (Exception e) {
                logger.error("redis分布式锁解锁异常,{}", e);
            }
        }
    }

    调用:

     //加锁
        long time = System.currentTimeMillis() + 1000 * lockTime //超时时间:10秒,最好设为常量
     
        boolean isLock = redisLock.lock(...keyName, String.valueOf(time));
        if(!isLock){
            throw new RuntimeException("系统正忙");
        }
        
        // doSomething...
        
        
        //解锁
        redisLock.unlock(...keyName, String.valueOf(time));

    更多Redis相关知识,请访问Redis使用教程栏目!

    以上就是redis会发生死锁问题吗的详细内容,更多请关注php中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    上一篇:redis一般存储什么类型数据 下一篇:redis是多线程的吗

    相关文章推荐

    • linux怎么进redis命令行操作• 怎么关闭redis• redis如何设置密码• 怎么实现锁定一个redis

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网