Java 使用Redis实现分布式锁

15 篇文章 0 订阅
4 篇文章 0 订阅

在高并发多线程的环境下,一些数据的存取操作会遇到许多的问题,导致数据污染

一般的情况下我们会给我们操作数据的代码上锁,就是加个 synchronized ,这样这块代码就是同步的了,每次只能有一个线程进入

那么数据自然不会被污染了

但是这样的操作也是存在问题的 1.由于变成单线程,这一块业务的操作变得非常缓慢,请求多的时候等待时间特别长

2.synchronized 只能在单服务器上使用,现在我们往往追求的是分布式的系统,synchronized 就用不了了

所以今天介绍一个加锁方法:使用 Redis 实现分布式锁

先介绍一下 Redis ,Redis 其实是一个 Key-Value 类型的数据库

它有两个操作

1. SETNX ,这是一个 set 操作,但是它会进行一个判断,  SETNX( key , value ) 的时候它会先查看以下这个 key 是否有值

如果无值,那么吧 value 设置进去,并返回 true

如果有值,放弃插入操作,并返回 false

2. GETSET ,这也是一个 set 操作,但是它会先取出原本这个 key 对应 value 值,也就是回返回旧的值

然后还有一点很关键的是, Redis 数据库可以设置生命时长,超过了时间数据就会失效

但是我们并不用它自带的这个来判断超时


做法:

1.首先我们定义一个 key 的名称作为我们这个锁对象

2.我们设置的 value = 当前系统时间 + 超时时间

private static final long TIMEOUT = 10 * 1000; // 超时时间10s
long time = System.currentTimeMillis() + TIMEOUT;
String value = String.valueOf(time);

3.先使用 SETNX 方法(java redis 里这个方法叫做 setIfAbsent() )把 key 和 value 设置进去

如果返回 true 则表示设置成功,之前没有人设置过

如果返回 false 则表示这个锁已经有人使用了,但是我们要判断它是否超时了,如果是已经超时了的话我们还是可以使用的

先取出这个 key 里的旧值,与当前时间比较,看当前时间是否已经超过了设置的值,如果超过了,那么就表示已经超时,我们可以设置新的值,我们使用 GETSET 方法设置新的值,把返回的旧值与我们之前取出来的旧值比较,如果一致的话表示我们成功更新了旧的值,获得了锁,如果不一致的话那么表示这个锁在这个期间又被别人获取走了,那么我们获取失败

4.使用完毕之后解锁:

取出 key 中的 value 值,与我们原先设置进去的进行比较,如果一致,表示这还是我们持有的锁,我们可以把这个 key delete掉了,但是如果取出的值与我们原本的值不一致,那么说明这个锁现在已经被别人获取了,那么我们就不能去删除别人的值了,直接退出操作就行了。


下面是封装好的 Redis 锁工具类

/**
 * @author: 林之谦
 * @date: 2018/8/5
 * @description:
 */
@Component
@Slf4j
public class RedisLock {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 加锁
     * @param key
     * @param value  当前时间 + 超时时间
     * @return
     */
    public boolean lock(String key,String value){
        //如果可以成功设置,可以加锁
        if(redisTemplate.opsForValue().setIfAbsent(key,value)){
            return true;
        }

        String currentVal = redisTemplate.opsForValue().get(key);
        // 如果锁过期
        if(!StringUtils.isEmpty(currentVal)
                && Long.parseLong(currentVal)  < System.currentTimeMillis()){
            // 获取上一个锁的时间
            String oldValue = redisTemplate.opsForValue().getAndSet(key,value);
            // 更新锁时间成功,则成功加锁
            if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentVal)){
                return true;
            }
        }
        return false;
    }

    public void unlock(String key,String value){
        try {
            String currentVal = redisTemplate.opsForValue().get(key);
            if(!StringUtils.isEmpty(currentVal) && currentVal.equals(value)){
                redisTemplate.opsForValue().getOperations().delete(key);
            }
        }catch (Exception e){
            log.error("【redis 分布式锁】 解锁异常, {}",e);
        }

    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值