redis分布式锁

分布式锁的奥义

占坑一般使用setnx指令,只允许被一个客户端占坑,先来先占,用完了再del

但是如果逻辑执行到中间除了异常,导致del操作没有被调用,就会陷入死锁,锁得不到释放

可以在拿到锁之后,给锁加上一个过期时间

但是如果在setnx和expire之间服务器进程突然关了,导致expire得不到执行,也会造成死锁

请添加图片描述

根源setnx和expire不是原子指令

Redis2.8中对set进行扩展,使得setnx和expire指令可以一起执行,解决了分布式锁的乱象

set lock:codehole true ex 5 nx

超时问题

如果在加锁和释放锁之间的逻辑执行得过长,超出了锁的超时限制,就会出现问题。

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

方案:将set指令的value参数设置为一个随机数,释放锁实现匹配一下随机数是否一致,然后再删除key,确保当前线程占有的锁不会被其他线程释放,除非这个琐是因为过期而被服务器自动释放

但是匹配value和删除key不是一个原子操作,需要用Lua脚本处理

可重入性

可重入性是指线程在持有锁的情况下再次请求加锁,如果一个锁支持同一个线程的多次加锁,那么这个锁是可重入的

public class RedisWithReentrantLock {
    private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<Map<String, Integer>>();//ThreadLocal加计数器

    private Jedis jedis;

    public RedisWithReentrantLock(Jedis jedis){
        this.jedis = jedis;
    }

    private boolean _lock(String key){
        return jedis.set(key, "", "nx", "ex", 5L) != null;
    }

    private void _unlock(String key){
        jedis.del(key);
    }

    private Map<String, Integer> currentLockers(){
        Map<String, Integer> refs = lockers.get();
        if(refs != null){
            return refs;
        }
        lockers.set(new HashMap<String, Integer>());
        return lockers.get();
    }

    public boolean lock(String key){
        Map<String, Integer> refs = currentLockers();
        Integer refCnt =refs.get(key);
        if(refCnt != null){
            refs.put(key,refCnt + 1);//+1
            return true;
        }
        boolean ok = this._lock(key);
        if(!ok){
            return false;
        }
        refs.put(key, 1);
        return true;
    }

    public boolean unlock(String key){
        Map<String, Integer> refs = currentLockers();
        Integer refCnt = refs.get(key);
        if(refCnt == null){
            return false;
        }
        refCnt -= 1;
        if(refCnt > 0){
            refs.put(key, refCnt);//-1
        } else {
            refs.remove(key);
            this._unlock(key);
        }
        return true;
    }

    public static void main(String[] args) {
        //连接redis服务器(在这里是连接本地的)
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        //权限认证
        //jedis.auth("");
        System.out.println("连接服务成功");
        RedisWithReentrantLock redis = new RedisWithReentrantLock(jedis);
        System.out.println(redis.lock("codehole"));
        System.out.println(redis.lock("codehole"));
        System.out.println(redis.unlock("codehole"));
        System.out.println(redis.unlock("codehole"));

    }

}

基于ThreadLocal和引用计数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值