redis锁的注意事项:
1、(key,value)= (uuid,threadID)
uuid保证锁的唯一,threadID保证当前线程的锁不会被其他线程释放掉。
2、 防止死锁 ,所以使用setnx命令设置一个失效时间
3、 防止提前释放锁(当前线程没有执行完成,超时时间到了导致锁被删除) 开启一个守护线程来加时(执行续命操作)
守护线程的好处,任务执行完,守护线程自动删除。
4、 单点故障,使用redlock方案解决
为什么要使用分布式锁?
首先看synchronized的作用范围就是在一个JVM中有效,如果我在俩个系统中同时去秒杀一个商品,synchronized时不能控制并发的进行。
所以我们现在将锁放到一个能让所有的系统都能访问到公共的区域,然后设置一个布尔值,如果我在秒杀商品将布尔值设置为1(就是让别人等着,等我买完你们在来),当我买商品完毕,我将布尔值设置为0,下一个人再来买。基于这样的思想,我们就可以想到,公共区域数据库,redis缓存,zookeeper这些放在公共区域大家一起用的都可以用来实现分布式锁的。至于其他俩个我就不再这里说了。
redis上锁方法实现
redis实现锁的优点是redis中可以设置变量的超时时间。这样就不用我们去考虑死锁的问题,你可以在设置锁的时候顺带加一个超时时间,到时间之后自动失效。
ThreadLocal类很重要,它在这里就是起一个保证一把锁一把钥匙的。
private ThreadLocal<String> threadLocal = new ThreadLocal<>();//线程安全问题,线程复用
/**
* 上锁
* @param key
* @param timeout
* @param unit
* @return
*/
public boolean tryLock(String key, long timeout, TimeUnit unit){
String uuid = UUID.randomUUID().toString();//用uuid进行唯一标识当前锁
threadLocal.set(uuid);
boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(key,uuid,timeout,unit);
while (!lock){
lock = stringRedisTemplate.opsForValue().setIfAbsent(key,uuid,timeout,unit);
if (lock){
break;
}
}
return lock;
}
解锁方法实现
/**
* 解锁
* @param key
*/
public void releaseLock(String key){
if (threadLocal.get().equals(stringRedisTemplate.opsForValue().get(key))){
stringRedisTemplate.delete(key);
}
}