1、关于redis分布式锁,有个setIfAbsent:
即如果没有设置,会添加分布式锁,并返回true;
2、redis分布式锁有个轮询过程:
/* @param key redis键* @param expire 键过期时间(单位:毫秒)* @param timeout 超时时间(单位: 毫秒) * @return true表示加锁成功,false表示加锁失败 */public boolean lock(String key, long expire, long timeout) { timeout *= 1000*1000; //一般用常量表示,进行毫秒与纳秒之间的转换 long nanoTime = System.nanoTime(); try { //在timeout的时间范围内不断轮询锁 while (System.nanoTime() - nanoTime < timeout) { //锁不存在的话,设置锁并设置锁过期时间,即加锁 Boolean isSuccess = redisTemplate.opsForValue().setIfAbsent(key, RedisLock.LOCKED); if (isSuccess) { //设置锁过期时间是为了在没有释放锁的情况下锁过期后消失,不会造成永久阻塞 redisTemplate.expire(key, expire, TimeUnit.MILLISECONDS); this.lock = true; return true; } //短暂休眠,避免可能的活锁 Thread.sleep(3, RANDOM.nextInt(30)); } } catch (Exception e) { throw new RuntimeException("locking error",e); } return false;}
3、设置分布式锁,一般而言,分布式锁只做判断,具体我们还要使用redis进行一个设置key的过程,这样也类似于另一把锁:
为什么要使用另一把锁呢?其实分布式锁只是为了解决几个程序间同时操作的问题,它的时间非常不好控制,当我们有了另一把锁就可以轻易地去控制锁的时间,
lockkey = RedisKeyConstants.LOCK_PREFIX + ":" + redisKey; redisLock.lock(lockkey, RedisKeyConstants.LOCK_ROUTE_GEN_EXPIRE_TIME, RedisKeyConstants.LOCK_ROUTE_GEN_TIMEOUT_TIME); //判断redis中是否有数据 String redisValue = redisTemplate.opsForValue().get(redisKey); if (redisValue != null) { return; } redisTemplate.opsForValue().set(redisKey, "1", RedisKeyConstants.ROUTE_TYPE_GEN_EXPIRE_TIME, TimeUnit.SECONDS);
4、程序结束后一定要在finally代码块中释放锁,这样就好了;
finally { redisTemplate.delete(redisKey); redisLock.unlock(lockkey); }
5、防止程序出现bug,所以我们设置另一把锁的失效时间为半个小时;
需要考虑的问题:
1、多台服务器的时间有误差;
2、人工触发;
3、服务器宕机;