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

另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。

可重入锁,是单体Redis推荐的,(集群式的Redis则推荐使用红锁),简单来说便是在获取到锁之后,调用一个新的线程去轮询(轮询间隔大概是锁超时时间的1/3)的查看是否已到超时时间,到了而线程还没执行完,就给这个锁续上时间以便它能拿到锁继续执行,保证不被别的线程抢走。

要使用Redisson,就当然要引入依赖,在spring-boot-starter中,也有相关的依赖,直接引入如下依赖就可以了。

<dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson-spring-boot-starter</artifactId>
     <version>${redisson.version}</version>
 </dependency>

作为演示,只使用最简单的单机式Redis,所以就添加一个Redisson的配置类就可以了。注入一个RedissonClient的bean,填好相关的地址和密码就可以了。

@Bean
    public RedissonClient redisson() throws IOException {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://"+url + ":" + port).setPassword(password);
        return Redisson.create(config);
    }

最后,再对我们的服务进行一些小的修改,把原先我们自己实现的分布式锁给替换成Redisson实现的分布式锁。

//标识锁用
        String lock = "phoneLock";
        //获取锁
        RLock rLock = redissonClient.getLock(lock);
        if (rLock.isLocked()) {
            System.out.println("活动太火爆了,请稍后再试!");
            return "活动太火爆了,请稍后再试!";
        }
        //获取到了,才需要finally去释放锁
        try {
            //加锁
            rLock.lock(5, TimeUnit.SECONDS);
            //取出phone的剩余数量
            String phone = redisTemplate.opsForValue().get("phone");
            if (phone == null) {
                System.out.println("没抢到...");
                return "没抢到...";
            }
            int number = Integer.parseInt(phone);
            //还有剩余
            if (number > 0) {
                //消费一个
                int newNumber = number - 1;
                //将消费完的phone的数量,重新放到redis中
                redisTemplate.opsForValue().set("phone", String.valueOf(newNumber));
                System.out.println("恭喜抢到啦!" + newNumber);
                return "恭喜抢到啦!";
            } else {
                System.out.println("没抢到...");
                return "没抢到...";
            }
        } finally {
            if (rLock.isLocked()) {
                //释放锁
                rLock.unlock();
            }
        }

然后同样的执行一下,输出结果如下。

redis毫秒过期 redisson过期时间_redis毫秒过期


通过Redisson,我们可以非常方便快捷的使用分布式锁。本项目就演示了简单的可重入锁的使用,不同场景需求Redisson也给我们提供了不同的锁,具体问题,具体分析需要什么锁。

最后再简单介绍一下各种锁吧:

  1. 可重入锁
    基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
    大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
    另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
  2. 公平锁
    它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。
  3. 联锁
    基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。
  4. 红锁(RedLock)
    基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。
  5. 读写锁(ReadWriteLock)
    分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
  6. 信号量(Semaphore)
    基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
  7. 可过期性信号量(PermitExpirableSemaphore)
    基于Redis的Redisson可过期性信号量(PermitExpirableSemaphore)是在RSemaphore对象的基础上,为每个信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。它提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
  8. 闭锁(CountDownLatch)
    基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。