分布式锁--Redis实现
2016-02-25 15:39
513 查看
实现原理:Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。redis的SETNX命令可以方便的实现分布式锁。
SETNX命令(SET if Not eXists) 语法: SETNX key value 功能: 当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
直接上代码:
上面的lock方法中会不断尝试的去获取锁,直到超时。可以对该方法进行改造:
SETNX命令(SET if Not eXists) 语法: SETNX key value 功能: 当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
直接上代码:
public class RedisLockService { /** 加锁标志 */ public static final String LOCKED = "TRUE"; /** 毫秒与纳秒的换算单位 1毫秒 = 1000000纳秒 */ public static final long MILLI_NANO_CONVERSION = 1000 * 1000L; /** 默认超时时间(毫秒) */ public static final long DEFAULT_TIME_OUT = 500; public static final Random RANDOM = new Random(); /** 锁的超时时间(秒),过期删除 */ public static final int EXPIRE = 10; private Logger logger = Logger.getLogger(RedisLockService.class); //private String key; // 锁状态标志 //private boolean locked = false; @Autowired private PriceCacheService cacheService; /** * 加锁 * 应该以: * lock(); * try { * doSomething(); * } finally { * unlock(); * } * 的方式调用 * @param timeout 超时时间 * @return 成功或失败标志 */ public boolean lock(String key, long timeout) { long nano = System.nanoTime(); timeout *= MILLI_NANO_CONVERSION; try { while ((System.nanoTime() - nano) < timeout) { //if (this.jedis.setnx(this.key, LOCKED) == 1) { //this.jedis.expire(this.key, EXPIRE); if (cacheService.setNX(key, LOCKED, EXPIRE)) { //cacheService对Redis进行了简单的封装 //this.locked = true; //return this.locked; return true; } // 短暂休眠,避免出现活锁 Thread.sleep(3, RANDOM.nextInt(500)); } } catch (Exception e) { throw new RuntimeException("Locking error", e); } return false; } /** * 加锁 * 应该以: * lock(); * try { * doSomething(); * } finally { * unlock(); * } * 的方式调用 * @param timeout 超时时间 * @param expire 锁的超时时间(秒),过期删除 * @return 成功或失败标志 */ public boolean lock(String key, long timeout, int expire) { long nano = System.nanoTime(); timeout *= MILLI_NANO_CONVERSION; try { while ((System.nanoTime() - nano) < timeout) { //if (this.jedis.setnx(this.key, LOCKED) == 1) { // this.jedis.expire(this.key, expire); if (cacheService.setNX(key, LOCKED, expire)) { //this.locked = true; //return this.locked; return true; } // 短暂休眠,避免出现活锁 Thread.sleep(3, RANDOM.nextInt(500)); } } catch (Exception e) { throw new RuntimeException("Locking error", e); } return false; } /** * 加锁 * 应该以: * lock(); * try { * doSomething(); * } finally { * unlock(); * } * 的方式调用 * @return 成功或失败标志 */ public boolean lock(String key) { return lock(key, DEFAULT_TIME_OUT); } /** * 解锁 * 无论是否加锁成功,都需要调用unlock * 应该以: * lock(); * try { * doSomething(); * } finally { * unlock(); * } * 的方式调用 */ public void unlock(String key) { try { //if (this.locked) { //this.jedis.del(this.key); cacheService.delNX(key); //this.locked = false; //} } catch(Exception e){ throw new RuntimeException("Locking error", e); } } }
上面的lock方法中会不断尝试的去获取锁,直到超时。可以对该方法进行改造:
public boolean acquire(String key){ try{ long expires = System.currentTimeMillis() + EXPIRE*1000 + 1; String expiresStr = String.valueOf(expires); //锁到期时间 if (cacheService.setNX(key, expiresStr)) { //设置key对应的value为该key的过期时间 // lock acquired return true; } String currentValueStr = (String)cacheService.getReal(key); //redis里的时间 if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) { //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的 // lock is expired String oldValueStr = cacheService.getSet(key, expiresStr); //获取上一个锁到期时间,并设置现在的锁到期时间, //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的 if (oldValueStr != null && oldValueStr.equals(currentValueStr)) { //如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁 // lock acquired return true; } } return false; }catch(Throwable t){ logger.error("[RedisLockService] acquire ERROR, key:"+key+", "+t.getMessage(), t ); return false; } }
相关文章推荐
- redis安装问题小结
- Redis偶发连接失败案例实战记录
- Redis中实现查找某个值的范围
- Redis和Memcached的区别详解
- 分割超大Redis数据库例子
- Redis总结笔记(一):安装和常用命令
- Redis sort 排序命令详解
- 用Redis实现微博关注关系
- redis中修改配置文件中的端口号 密码方法
- 在Ruby on Rails上使用Redis Store的方法
- Redis和Memcache的区别总结
- 在Node.js应用中使用Redis的方法简介
- Redis服务器的启动过程分析
- web 应用中常用的各种 cache详解
- 利用yum安装Redis的方法详解
- 从MySQL到Redis的简单数据库迁移方法
- 为啥懒 Redis 是更好的 Redis
- 利用Redis实现SQL伸缩的方法
- 在Redis数据库中实现分布式速率限制的方法
- PHP+redis实现添加处理投票的方法