Redis实现分布式锁

伪代码如下:

//整体流程
public void acquireLockMethod(){
    Map lockMap = new HashMap<>();
    try{
        lockMap = acquireLock(lockKey,expire);
        if(lockMap!=null){
            todoBusiness();//获得锁,执行业务逻辑方法
        }
    }catch(Exception e){
        throw e;
    }finally{
    releaseLock(lockKey,lockMap);
    }
}
//acquireLock()方法:
public Map acquireLock(String lock,long expired){
    Map map = new HashMap<>();
    long value = System.currentTimeMillis() + expired + 1;
    long acquired = jedis.setnx(lock, String.valueOf(value)); 
    if (acquired == 1)
        map.put("isSuccess",true);
        map.put("expireTimeStr":value);
        return map;
    else {
        long oldValue = Long.valueOf(jedis.get(lock));
        //如果其他资源之前获得锁已经超时                              
        if (oldValue < System.currentTimeMillis()){
            String getValue = jedis.getSet(lock, String.valueOf(value)); 
            //上一个锁超时后会有很多线程去争夺锁,所以只有拿到oldValue的线程才是获得锁的。
            if (Long.valueOf(getValue) == oldValue)
                map.put("isSuccess",true);
                map.put("expireTimeStr":value);
                return map;
            else
                return null;
            }    
        else return null ;
}
//解锁
private void release(ShardedJedis jedis, String lockKey,Map lockMap) {
        if(CollectionUtils.isEmpty(lockMap)){
            return;
        }
        Boolean locked = (Boolean) lockMap.get("isSuccess");
        String lockExpiresStr = (String) lockMap.get("expireTimeStr");
        if (locked) {
            String oldValueStr = jedis.get(lockKey);
            if (oldValueStr != null) {
                // 竞争的 redis.getSet 导致其时间跟原有的由误差,若误差在 超时范围内,说明仍旧是 原来的锁
                Long diff =  Long.parseLong(lockExpiresStr) - Long.parseLong(oldValueStr);
                if (diff < expireMsecs) {
                    jedis.del(lockKey);
                } else {
                    // 这个进程的锁超时了,被 新的进程锁获得替换了。则不进行任何操作。打印日志,方便后续跟进
                    log.error("the lockKey over time.lockKey:{}.expireMsecs:{},over time is", lockKey, expireMsecs, System.currentTimeMillis() - Long.valueOf(lockExpiresStr));
                }
            }
        }
    }

此分布式锁:
利用redis本身的单线程特性,以及setNX和getSet方法的原子特性,保证了多请求的加锁的互斥性,最后只有一个请求获得锁。
可以通过使用while循环实现阻塞特定时间的锁。
通过getSet方法,没有使用expire方法,保证了此分布式锁不会造成死锁。

此分布式锁依旧存在的问题:

  1. 如果业务逻辑执行时间超时(锁此时自动失效了),此时业务逻辑的数据已经不安全。虽然在解锁的时候,判断了锁的value值,做到了不会错误释放锁的情况,但是对超时业务逻辑只是做了日志记录,不太靠谱。
  2. redis集群部署时,这样的分布式锁会存在什么问题吗。这个没有试验过。

你可能感兴趣的:(Redis实现分布式锁)