redisson-分布式锁入门

在之前我介绍过如何保证缓存与数据库的双写一致性,说过使用分布式锁优化可以实现双写一致性,那么今天就来介绍一种具体实现方式redisson-分布式锁

原理

原理图如下:

图片[1]-redisson-分布式锁入门-编程社

主要分析如下:

何时加锁?

  • 线程获取锁,获取成功: 执行lua脚本,保存数据到redis数据库。
  • 线程去获取锁,获取失败: 一直通过while循环尝试获取锁,获取成功后,执行lua脚本,保存数据到redis

什么是WatchDog自动延期机制?

  • 在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,你也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生
  • 线程A业务还没有执行完,时间就过了,线程A 还想持有锁的话,就会启动一个watch dog后台线程,不断的延长锁key的生存时间

lua脚本

  • 主要是如果你的业务逻辑复杂的话,通过封装在lua脚本中发送给redis,而且redis是单线程的,这样就保证这段复杂业务逻辑执行的原子性

基本使用

RLock是继承Lock锁,所以他有Lock锁的所有特性,比如lock、unlock、trylock等特性,同时它还有很多新特性:强制锁释放,带有效期的锁

public interface RLock {
    
   //----------------------Lock接口方法-----------------------

    /**
     * 加锁 锁的有效期默认30秒
     */
    void lock();
    
     /**
     * 加锁 可以手动设置锁的有效时间
     *
     * @param leaseTime 锁有效时间
     * @param unit      时间单位 小时、分、秒、毫秒等
     */
    void lock(long leaseTime, TimeUnit unit);
    
    /**
     * tryLock()方法是有返回值的,用来尝试获取锁,
     * 如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false .
     */
    boolean tryLock();
    
    /**
     * tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,
     * 只不过区别在于这个方法在拿不到锁时会等待一定的时间,
     * 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
     *
     * @param time 等待时间
     * @param unit 时间单位 小时、分、秒、毫秒等
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
    /**
     * 比上面多一个参数,多添加一个锁的有效时间
     *
     * @param waitTime  等待时间
     * @param leaseTime 锁有效时间
     * @param unit      时间单位 小时、分、秒、毫秒等
     */
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;
    
    /**
     * 解锁
     */
    void unlock();
    
    
}

lock方法

**lock()**:此方法为加锁,但是锁的有效期采用默认30秒,如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制,如果主线程未释放,且当前锁调用unlock方法,则直接释放锁

 @GetMapping("/lock")
    public String lock() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("mylock");
        try {
           //加锁:锁的有效期默认30秒
            lock.lock();
            long timeToLive=lock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
            //休眠一下
            Thread.sleep(2000);

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }

注意:加锁:锁的有效期默认30秒

lock(long leaseTime, TimeUnit unit): 可以手动设置锁的有效时间,如果主线程未释放,且当前锁未调用unlock方法,则锁到期后会自动释放,如果主线程未释放,且当前锁调用unlock方法,则直接释放锁

 @GetMapping("/lockLaseTime")
    public String lockLaseTime() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("lockLaseTime");
        try {
            //这里可以手动设置锁的有效时间,锁到期后会自动释放的
            lock.lock(10,TimeUnit.SECONDS);
            long timeToLive=lock.remainTimeToLive();
            log.info("线程:{},获得锁,锁存活时间:{}S",Thread.currentThread().getName(),timeToLive/1000);
            //休眠一下
            Thread.sleep(2000);

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }

tryLock()方法

tryLock(): 用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制,如果主线程未释放,且当前锁调用unlock方法,则直接释放锁

 @GetMapping("/tryLock")
    public String tryLock() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("tryLock");
        try {
            //如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false .
            boolean flag=lock.tryLock();
            if(flag){
                long timeToLive=lock.remainTimeToLive();
                log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
                //休眠一下
                Thread.sleep(2000);
            }

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }

tryLock(long time, TimeUnit unit): tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间, 在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true,如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制如果主线程未释放,且当前锁调用unlock方法,则直接释放锁

 @GetMapping("/tryLockWaitTime")
    public String tryLockWaitTime() {
        //获得分布式锁对象,注意,此时还没有加锁成功
        RLock lock = redissonClient.getLock("tryLockWaitTime");
        try {
            //只不过区别在于这个方法在拿不到锁时会等待一定的时间,
            //在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。
            boolean flag=lock.tryLock(6, TimeUnit.SECONDS);
            if(flag){
                long timeToLive=lock.remainTimeToLive();
                log.info("线程:{},获得锁,锁存活时间:{}S,加锁状态:{}",Thread.currentThread().getName(),timeToLive/1000,flag);
                //休眠一下
                Thread.sleep(2000);
            }

        }catch (Exception ex){
            System.out.println("出现异常!!!");
        }finally {
            //如果主线程未释放,且当前锁未调用unlock方法,则进入到watchDog机制
            //如果主线程未释放,且当前锁调用unlock方法,则直接释放锁
            log.info("线程:{},释放锁",Thread.currentThread().getName());
        }

        return "OK";
    }

注意:tryLock(long time, TimeUnit unit),这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回true

SpringBoot项目依赖

 <!--redis-->
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
      <version>2.4.0</version>
  </dependency>
  <!--使用redisson作为分布式锁-->
  <dependency>
      <groupId>org.redisson</groupId>
      <artifactId>redisson</artifactId>
      <version>3.16.8</version>
  </dependency>

配置类和配置文件

  • 配置文件
spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: 127.0.0.1
    # Redis服务器连接端口
    port: 6379
    # Redis服务器链接密码(默认为空)
    password:
    jedis:
      pool:
        # 连接池最大链接数(负值表示没有限制)
        max-active: 20
        # 连接池最大阻塞等待时间(负值表示没有限制)
        max-wait: -1
        # 链接池中最大空闲链接
        max-idle: 10
        # 连接池中最小空闲链接
        min-idle: 0
    # 链接超市时间(毫秒)
    timeout: 1000
server:
  port: 8899
  • 配置类
@Configuration
public class RedissonConfig {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private String port;


    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port);
        final RedissonClient client = Redisson.create(config);
        return client;
    }
}
© 版权声明
THE END
喜欢就支持一下吧
点赞10赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容