Skip to content

Github 文档

一、可重入锁(Reentrant Lock)

  • 大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。
  • 为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期
  • 默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
java

/**
 * 方式一: 锁操作,操作库存
 *
 * @param number        数量
 * @param operationType 操作类型
 * @return {@link Integer}
 */
private Integer stockOperationByLock(Integer number, OperationType operationType) {
  String currentThreadName = Thread.currentThread().getName();
  long currentTimeMillis = System.currentTimeMillis();
  String threadName = currentThreadName + "-" + currentTimeMillis;
  // 获取锁
  RLock rlock = redissonClient.getLock(STOCK_LOCK_KEY); 

  long stockTotal = 0L;
  try {
    rlock.lock();   
    log.info("线程名称:{} => 获取锁是否成功: {}", threadName, true);
    // 验证: 库存
    Stock stock = this.verifyStockParamReturn(number, operationType);
    // 计算: 库存数量
    stockTotal = this.computeStockNum(threadName, stock.getQuantity(), number, operationType);
    log.info("线程名称:{} => 当前库存总数: {}", threadName, stockTotal);
    // 更新: 库存数量
    updateQuantity(1L, stockTotal);
  } finally {
    if (rlock != null) {
        rlock.unlock(); 
    }
  }
  return (int) stockTotal;
}
  • 通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了
java

/**
 * 方式二: 通过尝试获取锁操作,操作库存
 *
 * @param number        数量
 * @param operationType 操作类型
 * @return {@link Integer}
 */
private Integer stockOperationByTryLock(Integer number, OperationType operationType) {
  String currentThreadName = Thread.currentThread().getName();
  long currentTimeMillis = System.currentTimeMillis();
  String threadName = currentThreadName + "-" + currentTimeMillis;
  // 获取锁
  RLock rlock = redissonClient.getLock(STOCK_LOCK_KEY); 

  long stockTotal = 0L;
  try {
    // 尝试上锁, 锁的等待时间 10 秒, 获取锁后的执行时间为 30秒(默认)自动解锁
    boolean isLocked = rlock.tryLock(10, TimeUnit.SECONDS);   
    // 尝试上锁, 锁的等待时间 10 秒, 获取锁后的执行时间为 40 秒 自动解锁
    // boolean isLocked = rlock.tryLock(10, 40,TimeUnit.SECONDS);
    log.info("线程名称:{} => 获取锁是否成功: {}", threadName, isLocked);
    if (!isLocked) {
        throw new RuntimeException("系统繁忙, 请稍后再试!");
    }
    // 验证: 库存
    Stock stock = this.verifyStockParamReturn(number, operationType);
     // 计算: 库存数量
    stockTotal = this.computeStockNum(threadName, stock.getQuantity(), number, operationType);
    log.info("线程名称:{} => 当前库存总数: {}", threadName, stockTotal);
    // 更新: 库存数量
    updateQuantity(1L, stockTotal);
  } catch (InterruptedException e) {
    throw new RuntimeException(e);
  } finally {
    if (rlock != null) {
        rlock.unlock(); 
    }
  }
  return (int) stockTotal;
}
java
/**
 *
 * 方式三: 通过异步锁,同步更新Redis库存数量
 *
 * @param number        数量
 * @param operationType 操作类型
 * @return {@link Integer}
 */
private void syncUpdateRedisStockByLockAsync(Long stockTotal, OperationType operationType) {
    // 获取锁
    RLock rlock = redissonClient.getLock(STOCK_LOCK_KEY);
    try {
        // 尝试异步上锁, 锁的等待时间 10 秒, 获取锁后的执行时间为 30秒(默认)
        RFuture<Boolean> future = rlock.tryLockAsync(10, 30, TimeUnit.SECONDS);   
        // 加锁完成后 异步执行
         future.whenComplete((res, ex) -> {                                       
            // 更新 redis 库存信息
            log.info("【异步上锁-更新Redis库存】 获取锁是否成功:{} 当前线程名称:{} 当前线程ID:{} ", future.isDone(), Thread.currentThread().getName(), Thread.currentThread().getId());
            if (ex != null) {
                log.error("【异步上锁-更新Redis库存】异常信息:{}", ex.getMessage());
                return;
            }
            if (!future.isDone()) {
                log.error("【异步上锁-更新Redis库存】获取异步锁失败:{}",future.isDone());
                return;
            }
            stringRedisTemplate.opsForValue().set(STOCK_CACHE, stockTotal.toString());
            log.error("【异步上锁-更新Redis库存】key:{} 更新库存数:{}",STOCK_CACHE, stockTotal);
        });


    } finally {
        if (rlock != null) {
            rlock.unlock();   
          }
    }
    return;
}
  • 重入锁: 同一个锁key,在不同方法下的同一个当前线程中,可以获取锁并加锁。
    • 每一次重入次数,都会加 1。
    • 每一次释放次数,都会减 1.
java

/**
 * 方式二: 通过尝试获取锁操作,操作库存
 *
 * @param number        数量
 * @param operationType 操作类型
 * @return {@link Integer}
 */
private Integer stockOperationByTryLock(Integer number, OperationType operationType) {
    String currentThreadName = Thread.currentThread().getName();
    long currentTimeMillis = System.currentTimeMillis();
    String threadName = currentThreadName + "-" + currentTimeMillis;
    // 获取锁
    RLock rlock = redissonClient.getLock(STOCK_LOCK_KEY);     

    long stockTotal = 0L;
    try {
        // 尝试上锁, 锁的等待时间 10 秒, 获取锁后的执行时间为 30秒(默认)
        boolean isLocked = rlock.tryLock(10, TimeUnit.SECONDS);      
        log.info("线程名称:{} => 获取锁是否成功: {}", threadName, isLocked);
        if (!isLocked) {
            throw new RuntimeException("系统繁忙, 请稍后再试!");
        }
        // 验证: 库存
        Stock stock = this.verifyStockParamReturn(number, operationType);
        // 计算: 库存数量
        stockTotal = this.computeStockNum(threadName, stock.getQuantity(), number, operationType);
        log.info("线程名称:{} => 当前库存总数: {}", threadName, stockTotal);
        // 更新: 库存数量
        updateQuantity(1L, stockTotal);
        // 同步更新:redis 库存 (重入锁)  
        syncUpdateRedisStockByLockAsync(stockTotal, operationType);            

    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    } finally {
        if (rlock != null) {
            rlock.unlock();           
        }
    }
    return (int) stockTotal;
}

/**
 *
 * 方式三: 通过异步锁,同步更新Redis库存数量
 *
 * @param number        数量
 * @param operationType 操作类型
 * @return {@link Integer}
 */
private void syncUpdateRedisStockByLockAsync(Long stockTotal, OperationType operationType) {
    // 获取锁
    RLock rlock = redissonClient.getLock(STOCK_LOCK_KEY); 
    try {
      // 尝试异步上锁, 锁的等待时间 10 秒, 获取锁后的执行时间为 30秒(默认)
      RFuture<Boolean> future = rlock.tryLockAsync(10, 30, TimeUnit.SECONDS);  
      // 加锁完成后 异步执行
      future.whenComplete((res, ex) -> {
          // 更新 redis 库存信息
          log.info("【异步上锁-更新Redis库存】 获取锁是否成功:{} 当前线程名称:{} 当前线程ID:{} ", future.isDone(), Thread.currentThread().getName(), Thread.currentThread().getId());
          if (ex != null) {
              log.error("【异步上锁-更新Redis库存】异常信息:{}", ex.getMessage());
              return;
          }
          if (!future.isDone()) {
              log.error("【异步上锁-更新Redis库存】获取异步锁失败:{}",future.isDone());
          return;
          }
          stringRedisTemplate.opsForValue().set(STOCK_CACHE, stockTotal.toString());
          log.error("【异步上锁-更新Redis库存】key:{} 更新库存数:{}",STOCK_CACHE, stockTotal);
      });
    } finally {
      if (rlock != null) {
          rlock.unlock();   
      }
    }
    return;
}
  • 如图所属: 执行到 syncUpdateRedisStockByLockAsync() 方法后中的 getLock() 方法后, 可以看出锁次数 +1

二、公平锁(Fair Lock)

  • 它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程,所有请求线程会在一个队列中排队
  • 当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒
java
RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();
java
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();
java
RLock fairLock = redisson.getFairLock("anyLock");
fairLock.lockAsync();
fairLock.lockAsync(10, TimeUnit.SECONDS);
Future<Boolean> res = fairLock.tryLockAsync(100, 10, TimeUnit.SECONDS);

三、联锁(MultiLock)

  • 基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例
  • 方式一
java
RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();
  • 方式二
java
RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开
lock.lock(10, TimeUnit.SECONDS);

// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

四、红锁(RedLock)

  • 基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法
  • 该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例
java
RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

五、读写锁(ReadWriteLock)

  • 读写锁 RReadWriteLock: 允许同时有多个读锁一个写锁处于加锁状态
  • Java对象实现了java.util.concurrent.locks.ReadWriteLock接口
  • 方式一:
java
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
  rwlock.readLock().lock();
// 或
  rwlock.writeLock().lock();
  • 方式二:
java
// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();