首页 > 代码库 > Redis分布式锁实现
Redis分布式锁实现
直接上代码:
1 package cn.wywk.yac.comm.redis; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 6 import redis.clients.jedis.Jedis; 7 8 /** 9 * ClassName: redis分布式锁实现 <br/> 10 * date: 2017年2月17日 上午10:23:24 <br/> 11 * 12 * @author 13414wuxinyu 13 * @since JDK 1.8 调用示例: JedisPool pool=getPool(); Jedis 14 * jedis=pool.getResource(); RedisLock jedisLock = new 15 * RedisLock(jedis,"aa"); try { if (jedisLock.acquire()) { // 启用锁 16 * //执行业务逻辑 //.... //释放锁 jedisLock.release(jedis.get("aa")); } else { 17 * System.out.println("该线程未获得锁"); } } catch (Exception e) { // 分布式锁异常 18 * e.printStackTrace(); } finally { if (jedis != null) { try { //返回给连接池 19 * jedis.close(); } catch (Exception e) { e.printStackTrace(); } } } 20 */ 21 public class RedisLock { 22 23 protected final Logger log = LoggerFactory.getLogger(getClass().getName()); 24 25 /** 加锁标志 */ 26 private boolean locked = true; 27 28 public String lockKey; 29 30 private Jedis jedis; 31 32 /** 获取锁默认超时时间(毫秒) */ 33 private int timeoutMsecs = 500; 34 35 /** 锁的超时时间(毫秒),过期删除,默认10S */ 36 private int expireMsecs = 10000; 37 38 public RedisLock(Jedis jedis, String lockKey) { 39 this.jedis = jedis; 40 this.lockKey = lockKey; 41 } 42 43 public RedisLock(Jedis jedis, String lockKey, int timeoutMsecs) { 44 this(jedis, lockKey); 45 this.timeoutMsecs = timeoutMsecs; 46 } 47 48 public RedisLock(Jedis jedis, String lockKey, int timeoutMsecs, int expireMsecs) { 49 this(jedis, lockKey, timeoutMsecs); 50 this.expireMsecs = expireMsecs; 51 } 52 53 /** 54 * 加锁 55 */ 56 public boolean acquire() throws InterruptedException { 57 int timeout = timeoutMsecs; 58 synchronized (this) { 59 while (timeout >= 0) { 60 long expires = System.currentTimeMillis() + expireMsecs + 1; 61 String expiresStr = String.valueOf(expires); // 锁到期时间 62 if (jedis.setnx(lockKey, expiresStr) == 1) { 63 locked = true; 64 return true; 65 } 66 long SystemLong = System.currentTimeMillis(); 67 String currentValueStr = jedis.get(lockKey); // redis里的时间 68 if (currentValueStr != null) { 69 long currentLong = Long.parseLong(currentValueStr); 70 long timeoutlong = SystemLong - currentLong; 71 if (timeoutlong > expireMsecs) {// 原线程持有锁超时 72 jedis.getSet(lockKey, expiresStr); 73 locked = true; 74 return true; 75 } 76 } 77 timeout -= 100; 78 Thread.sleep(100); 79 } 80 } 81 return false; 82 } 83 84 /** 85 * 解锁 86 */ 87 public void release(String value) { 88 try { 89 release(jedis, value); 90 } catch (Exception e) { 91 e.printStackTrace(); 92 log.error(e.getMessage()); 93 } 94 } 95 96 private void release(Jedis jedis, String value) throws Exception { 97 if (locked) { 98 if (value.equals(jedis.get(lockKey))) {// 防止误释放其他线程的锁 99 jedis.del(lockKey); 100 locked = false; 101 } else { 102 throw new RuntimeException("Redis分布式锁释放失败,锁已被其他线程获得"); 103 } 104 } 105 } 106 107 }
测试代码:
1 package cn.wywk.yac.redistest; 2 import cn.wywk.yac.comm.redis.RedisLock; 3 import redis.clients.jedis.Jedis; 4 import redis.clients.jedis.JedisPool; 5 import redis.clients.jedis.JedisPoolConfig; 6 7 public class RedisLockTest implements Runnable{ 8 9 private static JedisPool pool = null; 10 11 private String xianName=""; 12 13 public RedisLockTest(){} 14 15 public RedisLockTest(String name){ 16 xianName=name; 17 } 18 19 /** 20 * 构建redis连接池 21 * 22 * @param ip 23 * @param port 24 * @return JedisPool 25 */ 26 public static JedisPool getPool() { 27 if (pool == null) { 28 JedisPoolConfig config = new JedisPoolConfig(); 29 //控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取; 30 //如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。 31 config.setMaxIdle(500); 32 //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。 33 config.setMaxIdle(5); 34 //表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException; 35 config.setMaxWaitMillis(1000 * 100); 36 //在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的; 37 config.setTestOnBorrow(true); 38 pool = new JedisPool(config, "127.0.0.1",6379); 39 } 40 return pool; 41 } 42 43 @Override 44 public void run() { 45 JedisPool pool=getPool(); 46 Jedis jedis=pool.getResource(); 47 RedisLock jedisLock = new RedisLock(jedis,"aa"); 48 try { 49 if (jedisLock.acquire()) { // 启用锁 50 System.out.println("线程名:"+xianName+"获得锁"); 51 //执行业务逻辑 52 //.... 53 //释放锁 54 System.out.println("线程名:"+xianName+"开始释放锁"); 55 jedisLock.release(jedis.get("aa")); 56 } else { 57 System.out.println("ERROR-线程名:"+xianName+"获取锁失败"); 58 } 59 } catch (Exception e) { 60 // 分布式锁异常 61 e.printStackTrace(); 62 } finally { 63 if (jedis != null) { 64 try { 65 //返回给连接池 66 jedis.close(); 67 } catch (Exception e) { 68 e.printStackTrace(); 69 } 70 } 71 } 72 } 73 74 public static void main(String[] args) throws InterruptedException { 75 for (int i = 0; i <40; i++) { 76 RedisLockTest test=new RedisLockTest("A线程"+i); 77 new Thread(test).start(); 78 RedisLockTest test2=new RedisLockTest("B线程"+i); 79 new Thread(test2).start(); 80 } 81 } 82 }
实现原理:
SETNX key value
功能:
当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
GETSET命令
语法:
GETSET key value
功能:
将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
我们将value存一个时间戳,如果setNx失败,判断该key是否超时,如果未超时,在获得锁的指定时间内等待锁
如果该锁超时,说明上客户端持有锁的时间超时了,或者前一个客户端被意外中断死锁。(此处默认持有锁最长时间是10S),使用getset命令更新锁
测试我们启动了80个线程进行测试,未发现问题!若有问题,请大家指正,谢谢
Redis分布式锁实现
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。