Redis 并发锁实现

  • Post author:
  • Post category:其他


听到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 版权协议,转载请附上原文出处链接和本声明。