• 记一个,生产遇到的redission锁,释放问题:lock.tryLock(0, 0, TimeUnit.SECONDS)


    package com.aswatson.cdc.test;
    
    import org.redisson.Redisson;
    import org.redisson.api.RLock;
    import org.redisson.api.RedissonClient;
    import org.redisson.config.Config;
    
    import java.time.LocalDateTime;
    import java.util.concurrent.*;
    
    /**
     *  boolean success = lock.tryLock(0, 0, TimeUnit.SECONDS); // 表示尝试获取锁,等待0秒,持有锁0秒钟
     *  注意问题,存在的隐患: 虽然 tryLock(0, 0, TimeUnit.SECONDS)
     *
     *  首先1. 但实际锁的释放仍然会受到 Redisson 看门狗机制的影响。如果持有锁的线程未能在续约周期内续约锁的持有时间,那么锁可能会在超时后被自动释放。
     *  (默认是每隔 30 秒进行一次续约)来维持锁的有效性,避免因为持有锁的线程未能释放而造成锁的永久占用。或者自己unLock。
     *
     *  其次2. 确保你使用的 Redisson 版本与 Redis 版本兼容,并且不会因为版本问题导致锁的行为异常。目前测试用的是redis(2.7.17)、redisson(3.24.3)
     *
     *  其次3. 默认情况下,Redisson 的看门狗会定期发送续约请求给 Redis 服务器,以延长当前持有的锁的有效期。但是也有不会续约的可能性:
     *         Redis 连接中断、Redisson 配置问题、持有锁的线程崩溃、锁的最大持有时间到期。
     *
     *  其次4.  即使在业务逻辑中调用了阻塞操作(如 sleep),Redisson 也会在后台继续进行续约操作,以防止锁被意外释放。
     *
     */
    public class TestRedissonLeaveTimeLock {
    
        public static void main(String[] args) throws Exception {
    
            Config config = new Config();
            config.useSingleServer().setAddress("redis://10.95.35.93:37495");
            RedissonClient redissonClient = Redisson.create(config);
            RLock lock = redissonClient.getLock("lockName");
            System.out.println("创建好了RedissonClient" + getName());
    
            int numThreads = 10;
            ExecutorService executor = Executors.newFixedThreadPool(numThreads);
            CountDownLatch startLatch = new CountDownLatch(1);
            CountDownLatch doneLatch = new CountDownLatch(numThreads);
    
            // 尽管 for 循环看起来是按顺序逐个,但实际上每个任务会并发地在后台执行。
            // 这是因为每次调用 submit时,任务被提交给线程池,而线程池会根据可用的线程资源并发执行这些任务。
            for (int i = 0; i < numThreads; i++) {
                executor.submit(() -> {
                    try {
                        startLatch.await(); // 等待主线程的启动信号
    
                        System.out.println("获取锁前的时间:"+ getName());
                        boolean success = lock.tryLock(0, 0, TimeUnit.SECONDS); // 尝试获取锁,等待0秒,持有锁0秒钟
                        System.out.println("获取锁后的时间:"+ getName());
                        if (success) {
                            System.out.println("拿到锁"+ getName());
                            // 模拟业务处理耗时 大于锁过期,可能导致非自己持有的锁被释放。
                            TimeUnit.SECONDS.sleep(60);
                        } else {
                            System.out.println("未能获取到锁,已放弃尝试" + getName());
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        doneLatch.countDown();// 每次减去1
    
                        // 判断当前线程是否持有锁
                        if (lock.isHeldByCurrentThread()) {
                            System.out.println("释放锁"+ getName());
                            lock.unlock();
                        }
                    }
                });
            }
    
            System.out.println("主线程即将释放所有等待的线程...");
            startLatch.countDown(); // 释放定义的1条线程,开始并发执行
            doneLatch.await(); // 等待所有线程10条完成
            executor.shutdown();
            System.out.println("所有线程执行完成" + getName());
        }
    
        public static String getName() {
            return Thread.currentThread().getName() + "---" + LocalDateTime.now();
        }
       //目前测试结果: 同时并发10条,9条拿不到锁,1条拿到锁,而且不会自动释放锁,即便LeaveTime = 0, 而且线程睡了60s,超过
       //看门狗机制的等待时间,也不会自动释放,而是打印了日志: 释放锁... 所以调用了阻塞操作(如 sleep),Redisson 也会在后台继续进行续约操作,以防止锁被意外释放。
    
    }
      <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.17</version>
        <relativePath/>
      </parent>
    
    <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-data-redis</artifactId>
          <version>2.7.17</version>
        </dependency>
        <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson-spring-data-27</artifactId>
          <version>3.24.3</version>
        </dependency>
        <dependency>
          <groupId>org.redisson</groupId>
          <artifactId>redisson-spring-boot-starter</artifactId>
          <version>3.24.3</version>
        </dependency>

    下面分析一下Redisson分布式锁源码:(注意版本差役,可能效果不一样)

     lock() 和 tryLock()都会用的 tryAcquireAsync(),看到区别,其实核心是leaseTime是否 > 0 

    leaseTime不大于0时,会执行scheduleExpirationRenewal(threadId);

     当oldEntry == null时,执行renewExpiration(); 就是个定时器,即是看门狗核心逻辑了。

     其中有下面红圈这段逻辑,也就是看门狗之所以在三分之一的时候执行的原因, 没有自定义时

     config.setLockWatchdogTimeout(10000); // 设置锁的看门狗超时时间为10秒

    默认就是30秒 除以3  也就是10秒时执行。

  • 相关阅读:
    PID学习
    语音增强用到的方法总结
    select语句查询数据 并新增字段列并赋值
    Flink——实时计算引擎
    测试内推 | 阿里飞猪、百度、58(招聘)、知乎、欢忻网络、百果园、阿里(Lazada)、深智城、元戎启行招人啦
    Redis的安装教程(Windows+Linux)【超详细】
    【ubuntu】搭建lamp架构
    Java基于SpringBoot的藏区特产销售系统的研究与实现
    MySQL 迁移完不能快速导数据了?
    RDPCrystal EDI SDK 10.0.4.X Crack
  • 原文地址:https://www.cnblogs.com/lgg20/p/18271553