本文共 4777 字,大约阅读时间需要 15 分钟。
获取锁:
@Override public String acquireLockWithTimeout(Jedis jedis,String lockName, long acquireTimeout, long lockTimeout) { //Jedis jedis =getJedis(); try { String identifier = UUID.randomUUID().toString(); String lockKey = "lock:" + lockName; int lockExpire = (int) (lockTimeout / 1000); long endTime = System.currentTimeMillis() + acquireTimeout; while (System.currentTimeMillis() < endTime) { logger.info("acquireLockWithTimeout->jedis->"+jedis); // 尝试设置key-value值,并且key不存在 if (jedis.setnx(lockKey, identifier) == 1) { // key不存在,并且设置成功,获取锁成功 jedis.expire(lockKey, lockExpire); return identifier; } //处理其他占用锁并且无法释放锁的情况 if (jedis.ttl(lockKey) == -1) { // key 存在但没有设置剩余生存时间 jedis.expire(lockKey, lockExpire); } try { Thread.sleep(1); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } } }catch(Exception e) { e.printStackTrace(); }finally { /*if(jedis!=null) { jedis.close(); }*/ } return null; }
流程图
释放锁:
@Override public boolean releaseLock(Jedis jedis,String lockName, String identifier) { //Jedis jedis = getJedis(); try { String lockKey = "lock:" + lockName; while (true) { // 监控key jedis.watch(lockKey); if (identifier.equals(jedis.get(lockKey))) { Transaction trans = jedis.multi(); trans.del(lockKey); List
删 除 的 时 候 需 要 注 意 先 监 控 k e y , 然 后 比 较 v a l u e , 再 删 除 , 之 所 以 需 要 监 控 , 是 因 为 再 比 较 后 可 能 值 会 出 现 变 化 \color{#FF0000}{删除的时候需要注意先监控key,然后比较value,再删除,之所以需要监控,是因为再比较后可能值会出现变化} 删除的时候需要注意先监控key,然后比较value,再删除,之所以需要监控,是因为再比较后可能值会出现变化
那 样 就 会 出 现 把 别 人 刚 刚 获 取 到 的 值 给 删 除 了 \color{#FF0000}{那样就会出现把别人刚刚获取到的值给删除了} 那样就会出现把别人刚刚获取到的值给删除了测试:
@RunWith(SpringRunner.class)@SpringBootTestpublic class LockServiceTest { private static final Logger logger = LoggerFactory.getLogger(LockServiceTest.class); @Autowired private RedisService redisService; @Autowired private RedisTemplateredisTemplate; private AtomicInteger atomicI = new AtomicInteger(0); @Test public void testJedis() { /*ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10); // for (int i = 0; i < 10; i++) { // 定时执行任务,每隔10秒钟执行一次 executorService.scheduleWithFixedDelay(new Runnable() { @Override public void run() { Jedis jedis = redisService.getJedis(); String value = jedis.get("aaa"); logger.info("=================【{}】=================", value); } }, 0, 10, TimeUnit.SECONDS); // }*/ }
@Test public void testLock() { ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { // 定时执行任务,每隔500毫秒执行一次 fixedThreadPool.execute(new Runnable() { @Override public void run() { Jedis jedis=null; atomicI.incrementAndGet(); String lockName = "lock01"; String id = ""; try { long acquireTimeout = 10000; long lockTimeout = 10000; long startTime = System.currentTimeMillis(); id = redisService.acquireLockWithTimeout(jedis,lockName, acquireTimeout, lockTimeout); long endTime = System.currentTimeMillis(); if (id != null) { logger.info("获取锁成功,id:【{}】花费时间:【{}】" ,id,(endTime - startTime)); } else { logger.info("获取锁失败,花费时间:【{}】",(endTime - startTime)); } Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } finally { if (id != "" && id != null) { logger.info("开始释放锁id:【{}】",id); redisService.releaseLock(jedis,lockName, id); } else { logger.info("无锁需要释放"); } } } }); } while(true) { if(fixedThreadPool.isTerminated()) { System.out.println("run over"); System.out.println("i="+atomicI); break; } } }}
如上的实现方式在主备、哨兵模式下会存在问题,比如在主节点执行了set后,然后宕机了,从节点同步获取到锁信息,那么其他客户端也会再次获取到锁。然后就出现了红锁。
官网中提到的红锁,大概流程为:向所有的节点发送获取锁的逻辑,如果有超过一半+1的实例返回ok,并且在时间节点内,那么就算获取锁成功,释放锁时也是一样,向所有的实例发送删除的命令。redission分布式锁的实现
参考:http://www.redis.cn/topics/distlock.html
摘自《Redis实战》。
参考:http://www.redis.cn/topics/distlock.html转载地址:http://juhvb.baihongyu.com/