redis 基本数据结构String篇:java应用篇(分布式锁)

最初原型

使用setnx (set if not exists)上锁(设置一个key value),待程序执行结束完成之后使用del 释放这个锁(删除 key)

死锁

如果逻辑执行到中间出现异常,会导致del没有调用,所以这把锁就会一直在redis中,陷入死锁

  • 解决方案:设置过期时间
expire yourKey 5 # 过期5s

早期的版本 setnx 和 expire不是原子性操作
但是在Redis 2.8版本中,作者加入了set指令的拓展参数, setnx 和 expire可以一起执行

set yourKey true ex 5 nx

超时问题

如果在加锁和释放锁之间的逻辑执行得太长,A线程的锁被redis过期指定释放锁,这时候如果B重新上锁,A代码逻辑执行结束,这时候执行释放锁的指令,就会出现A线程释放B线程的锁

  • 为了避免这个问题,Redis分布式锁不需要用于较长的时间的任务。

  • 另一个方案是将set指令的value参数设置为一个随机数,释放锁时先匹配随机数释放一致,然后再删除value,由于get锁匹配和del不是原子性的,redis也没有针对这两个的操作的单个原子指令,所以要使用lua脚本才能完整的实现这一功能

  • 如果超时问题是因为A线程执行时间过长,可以考虑在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程多次加锁,这就是锁的可重入。* Redis分布式锁如果要支持可重入,需要对客户端的set方法进行包装,使用线程的Threadlocal变量存储当前持有锁的计数

java代码实现

约定一些属性 获取连接

    private volatile Jedis jedis;
    //释放锁的关键value
    private volatile String unlockValue;

    public LockBase redisCil() {
        if (jedis == null) {
            log.info("redisCil is init");
            jedis = RedisCilBase.redisCil();
            return this;
        }
        return this;
    }

获取锁的方法,这里用到set nx ex 的混合指令

public Boolean lockOn(Object key, Integer time, Object value) {
        String keyStr = key.toString();
        String valueStr = value.toString();

        String valueRe = jedis.get(keyStr);
        log.info("redis valueRe:[{}]", valueRe);
        String result = jedis.set(keyStr, valueStr, SetParams.setParams().ex(time).nx());
        if (StringUtils.isEmpty(result)) {
            return false;
        }
        //上锁成功,放置一个解锁的value
        unlockValue = valueStr;
        return true;
    }

释放锁

 public Boolean unlock(Object key) {
        /*这里由于匹配value和删除key不是一个原子操作,这里比较安全的方法就是使用lua脚本*/
        String keyStr = key.toString();
        String valueStr = jedis.get(keyStr);

        if (StringUtils.isEmpty(valueStr)) {
            return true;
        }
        if (!valueStr.equals(unlockValue)) {
            return false;
        }
        return jedis.del(keyStr) == 1L;
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。
禁止转载,如需转载请通过简信或评论联系作者。