Redis分布式锁的应用html
GETSET方式java
http://doc.redisfans.com/string/getset.htmlredis
public final class RedisLockUtil { private static final int defaultExpire = 60; /** * 加锁 * @param key redis key * @param expire 过时时间,单位秒 * @return true:加锁成功,false,加锁失败 */ public static boolean lock(String key, int expire) { RedisService redisService = SpringUtils.getBean(RedisService.class); long status = redisService.setnx(key, "1"); //若是状态等于一 则 成功 返回值成功 if(status == 1) { redisService.expire(key, expire); return true; } //不然设置失败 return false; } public static boolean lock(String key) { return lock2(key, defaultExpire); } /** * 加锁 * @param key redis key * @param expire 过时时间,单位秒 * @return true:加锁成功,false,加锁失败 */ public static boolean lock2(String key, int expire) { RedisService redisService = SpringUtils.getBean(RedisService.class); long value = System.currentTimeMillis() + expire;//当时时间加过时时间 long status = redisService.setnx(key, String.valueOf(value));//尝试存一下 if(status == 1) { //若是能够直接存进去就 成功了 获取了锁 return true; } //若是没有设置成功 的后获取锁的value long oldExpireTime = Long.parseLong(redisService.get(key, "0")); if(oldExpireTime < System.currentTimeMillis()) { //超时 long newExpireTime = System.currentTimeMillis() + expire; //用getset覆盖 (getset理解为先get,再set。get的是旧的值,set的是新的值。) long currentExpireTime = Long.parseLong(redisService.getSet(key, String.valueOf(newExpireTime))); if(currentExpireTime == oldExpireTime) { return true; } } return false; } public static void unLock1(String key) { RedisService redisService = SpringUtils.getBean(RedisService.class); redisService.del(key); } public static void unLock2(String key) { RedisService redisService = SpringUtils.getBean(RedisService.class); long oldExpireTime = Long.parseLong(redisService.get(key, "0")); //oldExpireTime = 原当前时间 + 过时时间 其实不大于当前时间 都已通过期了 if(oldExpireTime > System.currentTimeMillis()) { redisService.del(key); } } }
在实际使用总大可能是注解,获取切面实现。并发
SETNX方式分布式
http://redisdoc.com/string/setnx.htmlide
如下是http://blog.csdn.net/u010359884/article/details/50310387 实现方式,通过使用和一些业务改造。this
public class CacheLockInterceptor implements InvocationHandler{ public static int ERROR_COUNT = 0; private Object proxied; public CacheLockInterceptor(Object proxied) { this.proxied = proxied; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { CacheLock cacheLock = method.getAnnotation(CacheLock.class); //没有cacheLock注解,pass if(null == cacheLock){ System.out.println("no cacheLock annotation"); return method.invoke(proxied, args); } //得到方法中参数的注解 Annotation[][] annotations = method.getParameterAnnotations(); //根据获取到的参数注解和参数列表得到加锁的参数 Object lockedObject = getLockedObject(annotations,args); String objectValue = lockedObject.toString(); //新建一个锁 RedisLock lock = new RedisLock(cacheLock.lockedPrefix(), objectValue); //加锁 boolean result = lock.lock(cacheLock.timeOut(), cacheLock.expireTime()); if(!result){//取锁失败 ERROR_COUNT += 1; throw new CacheLockException("get lock fail"); } try{ //加锁成功,执行方法 return method.invoke(proxied, args); }finally{ lock.unlock();//释放锁 } } /** * * @param annotations * @param args * @return * @throws CacheLockException */ private Object getLockedObject(Annotation[][] annotations,Object[] args) throws CacheLockException{ if(null == args || args.length == 0){ throw new CacheLockException("方法参数为空,没有被锁定的对象"); } if(null == annotations || annotations.length == 0){ throw new CacheLockException("没有被注解的参数"); } //不支持多个参数加锁,只支持第一个注解为lockedObject或者lockedComplexObject的参数 int index = -1;//标记参数的位置指针 for(int i = 0;i < annotations.length;i++){ for(int j = 0;j < annotations[i].length;j++){ if(annotations[i][j] instanceof LockedComplexObject){//注解为LockedComplexObject index = i; try { return args[i].getClass().getField(((LockedComplexObject)annotations[i][j]).field()); } catch (NoSuchFieldException | SecurityException e) { throw new CacheLockException("注解对象中没有该属性" + ((LockedComplexObject)annotations[i][j]).field()); } } if(annotations[i][j] instanceof LockedObject){ index = i; break; } } //找到第一个后直接break,不支持多参数加锁 if(index != -1){ break; } } if(index == -1){ throw new CacheLockException("请指定被锁定参数"); } return args[index]; } }
/** * 加锁 * 使用方式为: * lock(); * try{ * executeMethod(); * }finally{ * unlock(); * } * @param timeout timeout的时间范围内轮询锁 * @param expire 设置锁超时时间 * @return 成功 or 失败 */ public boolean lock(long timeout,int expire){ long nanoTime = System.nanoTime(); timeout *= MILLI_NANO_TIME; try { //在timeout的时间范围内不断轮询锁 while (System.nanoTime() - nanoTime < timeout) { //锁不存在的话,设置锁并设置锁过时时间,即加锁 if (this.redisClient.setnx(this.key, LOCKED) == 1) { this.redisClient.expire(key, expire);//设置锁过时时间是为了在没有释放 //锁的状况下锁过时后消失,不会形成永久阻塞 this.lock = true; return this.lock; } System.out.println("出现锁等待"); //短暂休眠,避免可能的活锁 Thread.sleep(3, RANDOM.nextInt(30)); } } catch (Exception e) { throw new RuntimeException("locking error",e); } return false; } public void unlock() { try { if(this.lock){ redisClient.delKey(key);//直接删除 } } catch (Throwable e) { } }
这也是你们经常使用的方式,可是这种方式的实现存在锁过时释放时,被正确释放。推荐GETSET。.net
https://www.cnblogs.com/yjf512/archive/2017/03/22/6597814.html线程
http://blog.csdn.net/u010648555/article/details/70139541指针
若是长时间获取不到,就会获取锁失败,至关于没加锁!
这里还有可能发生其余问题:
(1)并发状况,expire主动释放锁的时候,可能释放的是别人的锁;
好比,这个锁我上了10s,可是我处理的时间比10s更长,到了10s,这个锁自动过时了,被别人取走了,而且对它从新上锁了。那么这个时候,我再调用Redis::del就是删除别人创建的锁了。
(2)Redis服务挂掉,锁失败,至关于没加锁!最好使用主从+哨兵提升 高可用。集群。
Redis实现分布式锁全局锁—Redis客户端Redisson中分布式锁RLock实现