搜索
写经验 领红包

edis实现分布式锁好方案(edis分布式锁怎么实现)

导语:Redis实现分布式锁?不靠谱我之前有文章写个实现分布式锁的三种方式,但是当时只是草草介绍一下,这里详细介绍一下用Redis实现分布式锁。用Redis实现分布式锁,是Redis面试时的重要考点之一。很多同学都知道这个知识,也大致知道分布式锁的原理,但是具体到细节的使用上往往并不完全正确。(Wiki 解释:所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch 线程切换。)分布式锁的关键就是操作前先占坑。在redis中这个占坑操作用setnx,如果不存在就设置。如果已存在,会设置失败。第一个版本:> setnx xxlock trueOK... do something critical ...> del xxlock(integer) 1这有个严重问题,如果中间执行出异常了,del指令一直不会被调用。锁删不掉了,形成了死锁。第二个版本所以我们考虑到加一个超时时间。你可能会想到这样:> setnx xxlock trueOK> expire xxlock 5... do something critical ...> del xxlock(integer) 1这样写其实还是有问题,因为setnx 和expire 是两条指令。可能setnx 后因为极端异常情况导致expire 没有执行,而形成死锁。所以。我们希望这两条命令能作为一整个事务来执行。redis是支持事务的,但是redis的事务里不支持if这种逻辑判断。expire 应该只有在setnx 占坑成功才应该执行。针对这种情况,这里其实有一个简单办法解决:使用set的扩展语法> set lock:codehole true ex 5 nxOK... do something critical ...> del lock:codehole这个语法将setnx和ex超时设置变成一个原子操作,从根本上解决问题。加上超时就万无一失了吗?然而并不是。比如我们设置了超时时间5秒。如果业务代码5秒没执行完,但是锁已经到期释放了,这又出现并发问题了。目前没有找到好的解决办法。用redis实现的分布式锁不适合业务执行太长的情况。如果再考虑到redis使用集群模式,主备切换,节点宕机等情况会更加复杂。总之,用redis实现分布式锁不是非常安全。如果要实现可重入锁,代码将变得更加复杂。思路是使用ThreadLocal存储引用计数。附赠一个代码实现:public class RedisWithReentrantLock { private ThreadLocal<Map<String, Integer>> lockers = new ThreadLocal<>(); private Jedis jedis; public RedisWithReentrantLock(Jedis jedis) { this.jedis = jedis; } private boolean _lock(String key) { return jedis.set(key, "", "nx", "ex", 5L) != null; } private void _unlock(String key) { jedis.del(key); } private Map<String, Integer> currentLockers() { Map<String, Integer> refs = lockers.get(); if (refs != null) { return refs; } lockers.set(new HashMap<>()); return lockers.get(); } public boolean lock(String key) { Map<String, Integer> refs = currentLockers(); Integer refCnt = refs.get(key); if (refCnt != null) { refs.put(key, refCnt + 1); return true; } boolean ok = this._lock(key); if (!ok) { return false; } refs.put(key, 1); return true; } public boolean unlock(String key) { Map<String, Integer> refs = currentLockers(); Integer refCnt = refs.get(key); if (refCnt == null) { return false; } refCnt -= 1; if (refCnt > 0) { refs.put(key, refCnt); } else { refs.remove(key); this._unlock(key); } return true; } public static void main(String[] args) { Jedis jedis = new Jedis(); RedisWithReentrantLock redis = new RedisWithReentrantLock(jedis); System.out.println(redis.lock("codehole")); System.out.println(redis.lock("codehole")); System.out.println(redis.unlock("codehole")); System.out.println(redis.unlock("codehole")); }}

免责声明:本站部份内容由优秀作者和原创用户编辑投稿,本站仅提供存储服务,不拥有所有权,不承担法律责任。若涉嫌侵权/违法的,请反馈,一经查实立刻删除内容。本文内容由快快网络小心创作整理编辑!