分布式锁

来源:互联网 发布:钉钉网络异常 编辑:程序博客网 时间:2024/05/22 07:51

在分布式系统中,数据的一致性是个重要的问题,为了实现数据一致性,需要很多技术方案来支持,比如分布式锁等。分布式锁又有多种实现方式,如数据库乐观锁,redis分布式锁,还有zookeeper分布式锁。
这里介绍一下redis分布式锁的实现,直接看代码。

/** * 获取分布式锁 * * @param lockName           竞争获取锁key * @param acquireTimeoutInMS 获取锁超时时间 * @param lockTimeoutInMS    锁的超时时间 * @return 获取锁标识 */public String acquireLockWithTimeout(String lockName,                                     long acquireTimeoutInMS, long lockTimeoutInMS) {    logger.info("尝试获取锁,锁名:{}", lockName);    String retIdentifier = null;    String identifier = UUID.randomUUID().toString();    String lockKey = lockPrefix + lockName;    long end = System.currentTimeMillis() + acquireTimeoutInMS;    try {        BoundValueOperations<String, String> boundValueOperations = stringRedisTemplate.boundValueOps(lockKey);        while (System.currentTimeMillis() < end) {            boolean success = boundValueOperations.setIfAbsent(identifier);            if (success) {                //如果获得锁                boundValueOperations.expire(lockTimeoutInMS, TimeUnit.MILLISECONDS);                retIdentifier = identifier;                return retIdentifier;            }            //未设置锁的过期时间(可能是设置过期时间时发生异常导致)            if (boundValueOperations.getExpire() == -1) {                boundValueOperations.expire(lockTimeoutInMS, TimeUnit.MILLISECONDS);            }            try {                Thread.sleep(10);            } catch (InterruptedException ie) {                Thread.currentThread().interrupt();            }        }    } catch (Exception e) {        throw new BusinessException(ErrorCodeDefinition.ERROR_REDIS_GET,"获取分布式锁异常");        //在外层捕获到该异常时一般继续做业务处理    }    return retIdentifier;}/** * 释放锁 * * @param lockName   竞争获取锁key * @param identifier 释放锁标识 * @return */public boolean releaseLock(String lockName, String identifier) {    logger.info("尝试释放锁,锁名:{}", lockName);    String lockKey = lockPrefix + lockName;    boolean retFlag = true;    try {        BoundValueOperations<String, String> boundValueOperations = stringRedisTemplate.boundValueOps(lockKey);        if (identifier.equals(boundValueOperations.get())) {            stringRedisTemplate.delete(lockKey);            retFlag = true;        }    } catch (Exception e) {        retFlag = false;    }    return retFlag;}

在业务执行前后,分别获取分布式锁和释放分布式锁。当无法获取分布锁时,即表明已有其他进程/线程正在处理该业务。只有当锁被释放或者锁过期时才能进行新的业务调用。还有另外一种实现方式:
分布式锁实现方式2

public boolean tryLock(String lockKey) {    try{        Long time_stamp = System.currentTimeMillis();        BoundValueOperations<String, Long> boundValueOperations = redisTemplate.boundValueOps(lockKey);        boolean flag = boundValueOperations.setIfAbsent(time_stamp);        if(flag){//获得锁            redisTemplate.expire(lockKey, DEAD_LOCK_EXPIRED_SECONDS, TimeUnit.SECONDS);            return true;        }else{              //没有获得锁            //查看锁的时间戳进行死锁检查(可能存在锁一直未释放也没有过期时间的情况)            Long val = boundValueOperations.get();            if(val==null){            //如果key读取期间被删(释放锁),直接返回 false;                return false;            }else if((time_stamp - val) > DEAD_LOCK_REMOVE_SECONDS){ //已经超过时间             //当前锁已过期(可能是设置过期时间时出现异常导致)              //设置锁的新时间戳,并返回原时间戳(即锁的原标识)。               Long last_value = boundValueOperations.getAndSet(time_stamp);                if(val.equals(last_value) ){                     //相等则抢占到锁                    //重新设置过期时间                    redisTemplate.expire(lockKey, DEAD_LOCK_EXPIRED_SECONDS, TimeUnit.SECONDS);                   return true;                }else{                    return false; //时间戳已被其他线程                }            }else{ //未超时                return false;            }        }    }catch(Exception e){        LOG.error("获取分布式锁异常:{}",e);//当redis异常时,业务流程正常处理        return true;    }}public boolean unlock(String lockKey) {    try{        redisTemplate.delete(lockKey);    }catch(Exception e){        LOG.error("释放分布式锁异常:{}",e);        return false;    }    return true;}

当然,分布式锁也可以通过aop的方式实现。在需要加锁的地方添加分布式锁注解,然后在业务处理方法之前进行前置通知,执行加锁,在业务处理方法之后后置通知释放锁。

原创粉丝点击