Java使用Redis实现分布式锁
来源:互联网 发布:知乎怎么关注话题 编辑:程序博客网 时间:2024/06/05 05:12
思路
主要用的是redis的setnx()函数,利用函数的特性,即成功setnx的话返回1,如果key已经存在,setnx返回0,来判断能否将key插入到redis中。
但是要考虑锁无法正常释放的情况,避免成为垃圾锁,导致其他进程或线程无法进入加锁区域,需要特殊处理。通过redis的expire()函数来给key设置过期时间,当各种原因未能对锁unlock时,可以通过过期时间来实现锁的释放。
代码
package com.jikefriend.test.common.jedis;import org.apache.commons.lang.StringUtils;import org.apache.commons.lang.math.RandomUtils;import redis.clients.jedis.Jedis;public class RedisLock { /** 毫秒与毫微秒的换算单位 1毫秒 = 1000000毫微秒 */ private static final long MILLI_NANO_CONVERSION = 1000 * 1000L; private static final String EXPIRE_KEY_PREFIX = "lock:%s"; private boolean locked; private String key; public RedisLock(String key) { if (StringUtils.isEmpty(key)) { throw new IllegalArgumentException("Param key can not be null or empty"); } this.key = String.format(EXPIRE_KEY_PREFIX, key); this.locked = false; } /** * 获取锁 * 在timeout时间内会一直尝试获取锁,如果timeout=0,则表示获取失败后直接返回不再尝试 * 锁的有效期expireSecs必须大于0,当超过有效期未被unlock时,系统将会强制释放 * @param timeout 获取锁的等待时间 * @param expireSecs 锁的有效时间,必须大于0 * @return */ public boolean lock(long timeout, int expireSecs) { if (expireSecs <= 0) { throw new IllegalArgumentException("Param expireSecs must lager than zero."); } long nano = System.nanoTime(); timeout *= MILLI_NANO_CONVERSION; String lockStart = String.valueOf(System.currentTimeMillis()); Jedis jedis = JedisUtil.getResource(); try { while ((System.nanoTime() - nano) < timeout) { if (jedis.setnx(this.key, lockStart) == 1) { jedis.expire(this.key, expireSecs); this.locked = true; return this.locked; } Thread.sleep(3, RandomUtils.nextInt(500)); } String expireStr = jedis.get(key); Long now = System.currentTimeMillis(); String nowStr = String.valueOf(now); if (!StringUtils.isNumeric(expireStr)) { return false; } //ttl小于0 表示key上没有设置生存时间(key是不会不存在的,因为前面setnx会自动创建) //出现这种状况,是因为某个实例setnx成功后,expire由于各种可能原因而没有被调用造成的 //这时可以直接设置expire来占有锁 long ttlValue = jedis.ttl(this.key); if(ttlValue<0){ jedis.setnx(key, nowStr); jedis.expire(key, expireSecs); return true; } Long expireLong = Long.parseLong(expireStr); if (now - expireLong > expireSecs * 1000) { jedis.del(key); jedis.setnx(key, nowStr); jedis.expire(key, expireSecs); return true; } } catch (Exception e) { if (jedis != null) { JedisUtil.returnBrokenResource(jedis); } throw new RuntimeException("Locking error", e); } finally { if (jedis != null) { JedisUtil.returnResource(jedis); } } return false; } /** * 释放锁 * @return */ public boolean unLock() { Jedis jedis = JedisUtil.getResource(); try { if (jedis.del(this.key) == 1) { this.locked = false; return true; } return false; } catch (Exception e) { if (jedis != null) { JedisUtil.returnBrokenResource(jedis); } throw new RuntimeException("UnLocking error", e); } finally { if (jedis != null) { JedisUtil.returnResource(jedis); } } }}
如有考虑不妥的地方,还请大神指正。
0 0
- Java使用Redis实现分布式锁
- java使用redis实现分布式锁
- Java使用Redis实现分布式锁
- Java使用Redis实现分布式锁
- 使用 Redis 实现分布式锁
- 使用 Redis 实现分布式锁
- 使用Redis实现分布式锁
- 使用 Redis 实现分布式锁
- 使用redis实现分布式锁
- 使用redis实现分布式锁
- 使用Redis实现分布式锁
- 使用Redis实现分布式锁
- Redis分布式锁Java实现
- Redis分布式锁java实现
- redis中使用java脚本实现分布式锁
- 使用Redis SETNX 命令实现分布式锁
- 使用Redis如何实现分布式锁?
- 使用redis如何实现分布式锁
- bzoj 1485: [HNOI2009]有趣的数列 卡特兰数
- 1.3计算机系统的组织
- libevent源码深度剖析一
- input或textarea框动态限制字数
- Struts中实体类的字段名称要对应jsp中textfield中name属性值
- Java使用Redis实现分布式锁
- Sublime text 2/3 中 Package Control 的安装与使用方法
- leetcode16~3Sum Closest
- Android recovery分析(一)---全量升级包的编译流程
- Nginx优化
- 人机界面设计
- libevent源码深度剖析二
- 关于Div半透明子元素不透明的问题
- 继上次总结了点html标签,现在再写一些