redis分布式锁。
redis单线程理解:
redis操作基于命令,在一个命令执行过程中,其他命令处于等待排队状态。不可能同时执行两个命令。
IO多路复用:
多路网络连接复用一个io线程。
故redis很适合锁分布式锁
- 1.根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没有获取锁。
- 2.如果没有获得锁,去Redis上拿到该key对应的值,在该key上我们存储一个时间戳(用毫秒表示,t1),为了避免死锁以及其他客户端占用该锁超过一定时间(5秒),使用该客户端当前时间戳,与存储的时间戳作比较。
- 3.如果没有超过该key的使用时限,返回false,表示其他人正在占用该key,不能强制使用;如果已经超过时限,那我们就可以进行解锁,使用我们的时间戳来代替该字段的值。
- 4.但是如果在setnx失败后,get该值却无法拿到该字段时,说明操作之前该锁已经被释放,这个时候,最好的办法就是重新执行一遍setnx方法来获取其值以获得该锁
代码:
1 private static String lockKey = "myLockKey"; 2 3 /** 4 * value:当前时间ms值 + 超时时间(如3s) 5 * @param jedis 6 * @param value 7 * @return 8 */ 9 public boolean lock(Jedis jedis, String value){ 10 Long result = jedis.setnx(lockKey, value); 11 //添加成功 12 if(result == 1){ 13 return true; 14 } 15 String oldValue = jedis.get(lockKey); 16 //锁过期 17 if(!StringUtils.isEmpty(oldValue) && Long.parseLong(oldValue) < System.currentTimeMillis()){ 18 19 String val = jedis.getSet(lockKey, value); 20 //因为redis是单线程的,这些判断很有必要 21 if(!StringUtils.isEmpty(val) && val.equals(value)){ 22 return true; 23 } 24 } 25 return false; 26 27 28 } 29 30 /** 31 * 32 * @param jedis 33 * @param value:与获取锁的value一致 34 */ 35 public void unLock(Jedis jedis, String value){ 36 try{ 37 String old = jedis.get(lockKey); 38 if(!StringUtils.isEmpty(old) && old.equals(value)){ 39 jedis.del(lockKey); 40 } 41 }catch (Exception e){ 42 e.printStackTrace(); 43 } 44 45 46 }
setnx:若存在,则不添加,返回0
若不存在,添加成功,返回1