Redis实现分布式锁
1 问题导入
使用setnx来实现分布式锁?
2 问题分析
问题1
:setnx命令在参数中不能设置过期时间,要执行expire才能进行过期时间设置,不是原子性的操作,可能会在执行setnx后服务器宕机,没有设置expire,从而造成死锁现象
解决:在redis中可以使用set key value ex expireTime nx 命令来进行替换,在代码中可以使用redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS)来实现
问题2
:当A线程获取锁正在执行时,锁过期了,这时,线程B获取到了锁进入执行,当A线程执行完操作删除锁,实际是删除的B线程持有的锁(出现误删的情况)
解决:可以在设置锁的时候以线程id进行标识,删除时先进行判断,然后再进行删除
问题3
:先进行key的判断,再删除key的操作不是原子性的
解决:设置守护线程,在key过期之前进行续时,线程业务执行完后才能进行删除,即使当前线程所在服务器宕机该key也会自动过期释放,不会出现死锁
3 实现原理
- 1.使用set key value ex expireTime nx命令,此命令是原子性操作,在key不存在的情况下,进行set操作,同时给该key设置一个过期时间
- 2.应用中可以使用setIfAbsent(key, value, expireTime, TimeUnit.SECONDS)来实现
- 3.添加一个守护线程来进行key的监控以延长过期时间(续命),直到业务执行完毕
4 具体实现
4.1 加锁
/**
* 分布式锁(加锁)
* (setIfAbsent+后台守护线程为其续时)
* @param key 键
* @param value 值
* @param expire 过期时间
* @return true 成功,false 失败
*/
public boolean setIfAbsent(String key,Object value,long expire){
Boolean flag =false;
try{
if(expire>0){
flag = redisTemplate.opsForValue().setIfAbsent(key, value, expire, TimeUnit.SECONDS);
}
}catch (Exception e){
e.printStackTrace();
}
return flag;
}
4.2 续时
/**
* 分布式锁(续命)
* 通过守护线程进行key的续时
* @param key
* @param expire
*/
public void setExpire(String key ,long expire){
log.info("===>进入setExpire,进行续时操作&