redis(千帆竞发--分布式锁)

比如一个操作要修改的用户的状态,修改状态需要先读出用户的状态,在内存里进行修改,改完了再存回去。

如果这样的操作同时进行了,就会出现并发问题,因为读取和保存状态这两个操作不是原子的。

分布式锁:

分布式锁本质上要实现的目标就是在Redis里面占“坑”,当别的进程也要来占时,发现已经有人蹲在那里了,就

只好放弃或者稍后再试。

占坑一般是使用setnx(set if not exists)指令,只允许被一个客户端占坑。先来先到,用完了在调用del指令释放。

>setnx   lock:codehole   true
OK
...do  something  critical...
>del  lock:codehole
(integer) 1

但有个问题,如果逻辑执行到中间出现异常,可能会导致del指令没有被调用,这样就会陷入死锁,锁就得不到释放了。

这样我们可以拿到锁之后,再给锁加上一个过期时间,for example:5s

>setnx  lock:codehole  true
OK
>expire lock:codehole 5
...do something critical...
>del lock:codehole
(integer) 1

如果在setnx和expire之间服务器进程突然挂掉的,就会导致expire得不到执行,也会造成死锁。这个问题根源在于setnx和expire是两条指令而不是原子指令。如果这两条指令可以一起执行就不会出现问题。

>set lock:codehole true ex 5 nx
OK
...do something critical...
>del  lock:codehole

上面这个指令就是setnx和expire组合一起的原子指令,它就是分布式锁奥义的所在。

 

 

超时问题:

Redis的分布式锁解决超时问题,如果在加锁和释放锁之间的逻辑执行的太长,以至于超出了锁的超时限制,就会出现问题。因为

这时候第一个线程持有的锁过期了,临界区的逻辑还没有执行完,这个时候第二个线程就提前重新持有了这把锁,导致临界区代码不能得到严格的串行执行。

lua 脚本

if  redis.call("get",keys[1] == argv[1] then  

return redis.call("del",keys[1]))

else 
    return  0
end

可重入性:

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

public class RedisWithReentrantLock{
    private  ThreadLocal<Map<String,Integer>> lockers=new  ThreadLocal<>();
    private  Jedis  jedis;
    public   RedisWithReentrantLock(Jedis jedis){
            this.jedis=jedis;
     }
    private  boolean  lock(String key){
        return jedis.set(key,"","nx","ex",5L)
     }
    private  void  unlock(){
        jedis.del(key);
    }
    private Map<String,Integer> currentLockers(){
        Map<String,Integer>  refs=lockers.get();
    if(refs!=null){
        return refs;
      }
    lockers.set(new HashMap<>());
    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);
        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);
        }else{
            refs.remove(key);
            this.unlock(key);
        }
    return true;
    }
public static void main(String[] args){
        Jedis jedis=new Jedis();
        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"));
    }
}


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

brid_fly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值