在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言看了很多博客,和资料,这里只针对redis做分布式锁做一下深入探讨,希望对你们有帮助。网上提供了很多分布式锁的操作,这里逐一举例然后评论优缺点及改进方案,希望这样子能让当家更好的理解redis分布式锁。 redis分布式锁第一版大家应该都知道Redis做分布式锁无非就是INCR命令或者是SetNx命令,这里我们采用setnx命令。 缺点:如果这个人拿到锁后宕机了怎么办,那么这个锁就再也不能释放了。 改进:给这个锁增加一个过期时间,这样如果有效期过了,那么这个锁就会自动释放了。 redis分布式锁第二版通过上面所说我们应该对redis分布式进行改进。 redis分布式锁第三版通过上面的总结这第三版想必也很简单了。知识多了一个唯一值而已。但是加了唯一值还是改变不了锁不住的结果,只是解决了帮其他的线程解锁的问题,那么要怎么样才能锁得住呢?当时我想到的是给他 时间久一点,后来发现其实再久,也一样会出现锁不住的时候,而且太久了如果宕机了,就会有很长时间机器无法工作,很容易造成线程堆积。 redis分布式锁最终版由上面我们发现一般简单实用redis做锁其实是有很多漏洞和bug的,但是有没有能够解决这些的呢?当然是有的。 最终版还是打算先上代码再说,为了方便我把所有的实现都写在了一个类里面。 @Autowired private RedisTemplate redisTemplate; @Autowired private RedisUtils redisUtils; @Autowired(required = false) private ThreadPoolTaskScheduler threadPoolTaskScheduler; public final String LOCK_PREFIX = "REDIS_LOCK"; private final Long LOCK_EXPIRE = 30 * 1000L; private final Long OVER_TIME = 10L; private Map<String,ScheduledFuture<?> > futureMap = new ConcurrentHashMap<>(); private Jedis jedis; public Lock() { } private ReentrantLock reentrantLock; /** * 给线程枷锁 * * @param key */ public void lock(String key) { //自旋获取锁 while (true) { if (setLock(key)) {//拿锁成功 //获取锁后开启任务 threadPoolTaskScheduler.schedule(()->{ Set<String> keys = scan(LOCK_PREFIX); Iterator<String> iterator = keys.iterator(); //遍历所有的key 延长key的时间 while (iterator.hasNext()) { log.info("执行动态定时任务: " + LocalDateTime.now().toLocalTime()); redisUtils.expire(key, Long.valueOf(OVER_TIME), TimeUnit.SECONDS);//延长时间(秒) } },new Trigger(){ @Override public Date nextExecutionTime(TriggerContext triggerContext){ return new CronTrigger("0/10 * * * * ?").nextExecutionTime(triggerContext); } }); return; } } } /** * setnx * * @param key * @return */ public boolean setLock(String key) { String lock = LOCK_PREFIX + key; return (Boolean) redisTemplate.execute(new RedisCallback<Object>() { @Override public Object doInRedis(RedisConnection redisConnection) throws DataAccessException { long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1; Boolean acquire = redisConnection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes()); if (acquire) { return true; } else { byte[] value = redisConnection.get(lock.getBytes()); if (Objects.nonNull(value) && value.length > 0) { long expireTime = Long.parseLong(new String(value)); if (expireTime < System.currentTimeMillis()) { // 如果锁已经过期 byte[] oldValue = redisConnection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes()); // 防止死锁 return Long.parseLong(new String(oldValue)) < System.currentTimeMillis(); } } } return false; } }); } /** * 删除锁 * * @param key */ public void unlock(String key) { String lock = LOCK_PREFIX + key; synchronized (this) { futureMap.get(lock).cancel(true);//停止任务 redisTemplate.delete(lock); } } /** * 判断key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public boolean hasKey(String key) { try { return redisTemplate.hasKey(key); } catch (Exception e) { e.printStackTrace(); return false; } } public Set<String> scan(String key) { return (Set<String>) redisTemplate.execute((RedisCallback<Set<String>>) connection -> { Set<String> keys = Sets.newHashSet(); JedisCommands commands = (JedisCommands) connection.getNativeConnection(); MultiKeyCommands multiKeyCommands = (MultiKeyCommands) commands; ScanParams scanParams = new ScanParams(); scanParams.match("*" + key + "*"); scanParams.count(1000); ScanResult<String> scan = multiKeyCommands.scan("0", scanParams); while (null != scan.getStringCursor()) { keys.addAll(scan.getResult()); if (!StringUtils.equals("0", scan.getStringCursor())) { scan = multiKeyCommands.scan(scan.getStringCursor(), scanParams); continue; } else { break; } } return keys; }); } 分析:
加锁运行原理:
解锁操作原理:
解锁操作就比较简单了。但是得为了不出必要的麻烦,最好是给停止锁延时任务,和删除所 这两部添加进程锁,可以使用synchronized,也可以使用AQS lock锁。 这里Redis非公平锁详解算是结束了,后期可能会更新使用Redis,实现公平锁,谢谢大家的支持,如果有需要的小伙伴可以直接拿走,希望能给大家带来帮助。 在这里我希望看过文章的小伙伴能够根绝实现原理自己去实现,这样可以帮助小伙伴理解非公平锁机制,和Redis实现非公平,如果不喜欢自己去实现的话,这里我给大家推荐一个Redission 这个插件,这个插件是一个Redis锁的很好的一个实现,大家可以直接用这个。具体怎么用就不讲解了,操作非常简单。 到此这篇关于Redis分布式非公平锁的使用的文章就介绍到这了,更多相关Redis分布式非公平锁内容请搜索极客世界以前的文章或继续浏览下面的相关文章希望大家以后多多支持极客世界! |
请发表评论