三、redis分布式锁

135 阅读2分钟

一、分布式锁简介


1、分布式锁是什么?

分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁实现

如果不同的系统或者同一个系统的不同主机之间共享了某个资源时,往往通过互斥来防止彼此的干扰

2、分布式锁设计的目的是什么?

在分布式部署的集群应用中,可以保证同一个方法或属性(同一操作)同一时间只能被一个线程执行

3、分布式锁的应用场景

4、分布式锁的设计要求

  • 在分布式系统环境下,一个方法(一个操作)在同一时间只能被一台机器上的一个线程执行
  • 高可用的获取锁和释放锁
  • 高性能的获取锁和释放锁
  • 具备可重入特性
  • 具备锁失效机制,防止死锁
  • 具备非阻塞特性,即没有获取到锁将直接返回获取锁失败

5、分布式锁的特性有哪些

  • 互斥性:任意时刻只能有一个客户端拥有锁,不能同时多个客户端获取该锁
  • 安全性:锁只能被持有该锁的客户端删除,而不能被其他用户删除
  • 避免死锁:获取锁的客户端因为某些原因而宕机,未能释放锁,导致其它客户端无法获取该锁
  • 容错:当部分节点宕机,客户端仍能获取锁或者释放锁

二、分布式锁的实现


1、redis实现分布式锁

@RequestMapping(value = "deduct_stock")
public String deductStock(){
    synchronized (this){
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if(stock > 0){
            int realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock",String.valueOf(realStock));
            System.out.println("扣减成功,剩余库存:" + realStock);
        }else{
            System.out.println("扣减失败,库存不足");
        }
        return "end";
    }
}

synchronized关键字不能解决分布式的问题

@RequestMapping(value = "deduct_stock")
public String deductStock(){
    String lockKey = "lockKey";
    try{
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"cyan",10,TimeUnit.SECONDS);
        LOGGER.info("result="+result);
        if (!result){
            return "1001";
        }
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if(stock > 0){
            int realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock",String.valueOf(realStock));
            System.out.println("扣减成功,剩余库存:" + realStock);
        }else{
            System.out.println("扣减失败,库存不足");
        }
    }catch (Exception e){
        LOGGER.error("lock error");
    }finally {
        stringRedisTemplate.delete(lockKey);
    }
    return "end";
}

还存在一个线程的锁可能被其他线程的锁释放的问题

@RequestMapping(value = "deduct_stock")
public String deductStock(){
    String lockKey = "lockKey";
    String clientId = UUID.randomUUID().toString();
    try{
        //Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,"cyan");
        //stringRedisTemplate.expire(lockKey,10, TimeUnit.SECONDS);
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey,clientId,10,TimeUnit.SECONDS);
        LOGGER.info("result="+result);
        if (!result){
            return "1001";
        }
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if(stock > 0){
            int realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock",String.valueOf(realStock));
            System.out.println("扣减成功,剩余库存:" + realStock);
        }else{
            System.out.println("扣减失败,库存不足");
        }
    }catch (Exception e){
        LOGGER.error("lock error");
    }finally {
        if(clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){
            stringRedisTemplate.delete(lockKey);
        }
    }
    return "end";
}

还存在一个线程加锁后,锁过期时间内没有执行完,导致另一个线程也进入了同一代码块

2、redisson实现分布式锁

redisson框架实现分布式锁:

@RequestMapping(value = "deduct_stock")
public String deductStock(){
    String lockKey = "lockKey";
    RLock rLock = redisson.getLock(lockKey);
    try{
        //加锁实现锁续命功能
        rLock.lock();
        int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));
        if(stock > 0){
            int realStock = stock - 1;
            stringRedisTemplate.opsForValue().set("stock",String.valueOf(realStock));
            System.out.println("扣减成功,剩余库存:" + realStock);
        }else{
            System.out.println("扣减失败,库存不足");
        }
    }finally {
        rLock.unlock();
    }
    return "end";
}