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、服务器宕机;