听到Redis 实现并发锁,大家应该都很熟悉了,不知道有多少同学踩过redis并发锁的坑。
最近项目中有同学实现了并发锁,通过代码review还是发现有些同学理解的并不深入,为后续的运行埋下来了巨大的隐患,今天空闲之余再重温一下并发锁,希望多刚接触redis 锁的同学有启发。
首先列举一下几种常见的写法啊
1、第一种,也是在review代码时经常遇到的
Jedis jedis;
public boolean tryLock(String key){
String value = jedis.get(key);
if(org.apache.commons.lang3.StringUtils.isEmpty(value)){
jedis.set(key,"1");
jedis.expire(key,1000);
return true;
}
return false;
}
这种写法有几个问题呢?
1 String value = jedis.get(key); 多线程高并发访问时,拿到的value都为空都能拿到锁,并发控制失效
2 jedis.set(key,"1") ;jedis.expire(key,1000); 非原子操作,设置失效时间失败时,其他线程将永远获取不到锁。造成死锁。
2、第二种也是经常遇到的,还是先上代码才有说服力
Jedis jedis;
public boolean tryLock(String key){
long value = jedis.incr(key);
// 已经有其他线程获取到锁
if(value != 1)
{
return false;
}else
{
jedis.expire(key,100);
return true;
}
}
这种写法又有什么问题呢?
首先和第一个有相似之处的是,设置redis失效时间不是原子操作,中间任何异常导致设置失效时间出错时,会造成后续的死锁
看了这几个,有没有踩过同样的坑呢?
首先我认为写redis 并发锁,有几个坚持的原则
A、key和redis的失效时间是不是原子操作
B、get–》compare的方法在高并发场景写不可取
3、
三是我们最常用的,能满足我们常规加锁需求
Jedis jedis;
// 加锁
public boolean tryLock(String key){
String result = jedis.setex(key,1000,"1");
if(StringUtils.equals("OK",result)){
return true;
}
return false;
}
// 释放锁
public void releaseLock(String key){
jedis.del(key);
}
这里的实现思路是客户端进来加锁、任务执行完毕后释放锁,看上去这种方式是完美的。但......我们redis 设置都是主备的,
Client A 在master上得到锁,此时redis master Down机,redis将切换到Slave机器,Client B在此时去获取锁,
此时Client A的锁信息还有同步到Slave,这种情况Client A、B 都将获取到并发锁。
对于这种情况大家还是多看看redis 官方对并发锁的实现讲解。java 版本的开源实现并发锁Redisson
注:以上代码均为伪代码,有问题大家可以留言讨论,也希望文章对看到的人有些许用途
版权声明:本文为cliuyang原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。