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

redission,lock,trylock,timeunit,seconds · 浏览次数 : 0

小编点评

本文主要介绍了使用Redisson库实现分布式锁的示例代码,重点关注了锁的获取和释放过程,以及可能存在的问题和注意事项。 1. **代码结构**: - 示例代码包含一个`TestRedissonLeaveTimeLock`类,该类用于演示Redisson锁的使用。 - 类中定义了一个`main`方法,用于启动多个线程并发执行任务。 - 任务中尝试获取锁,并在获取成功或失败后执行相应的业务逻辑。 2. **锁的获取与释放**: - 使用`tryLock`方法尝试获取锁,参数设置为0秒等待时间和0秒持有时间。 - 锁的释放依赖于`lock.isHeldByCurrentThread()`方法,确保只有当前线程能够释放锁。 3. **潜在问题**: - Redisson的看门狗机制可能会在锁有效期内自动续约,但如果线程崩溃或网络中断,可能导致锁无法被释放。 - 使用Redisson时应注意与Redis服务器的版本兼容性,以避免因版本差异导致的异常行为。 - Redisson的锁持有期间,即使业务线程执行了阻塞操作(如sleep),Redisson后台仍会进行续约操作。 4. **线程安全考虑**: - 示例代码中未处理并发问题,实际应用中应考虑使用Redisson提供的同步原语(如`Semaphore`、`CountDownLatch`等)来确保线程安全。 总的来说,本文通过一个具体的代码示例,展示了Redisson锁的使用方法和注意事项,为开发者提供了关于分布式锁管理的深入理解。

正文

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(20);
                    } 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();
    }

}

 

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

记一个,生产遇到的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.redisso

记一次 .NET某网络边缘计算系统 卡死分析

一:背景 1. 讲故事 早就听说过有什么 网络边缘计算,这次还真给遇到了,有点意思,问了下 chatgpt 这是干嘛的 ? 网络边缘计算是一种计算模型,它将计算能力和数据存储位置从传统的集中式数据中心向网络边缘的用户设备、传感器和其他物联网设备移动。这种模型的目的是在接近数据生成源头的地方提供更快速

记一次 .NET 某安全生产信息系统 CPU爆高分析

一:背景 1.讲故事 今天是🐏的第四天,头终于不巨疼了,写文章已经没什么问题,赶紧爬起来写。 这个月初有位朋友找到我,说他的程序出现了CPU爆高,让我帮忙看下怎么回事,简单分析了下有两点比较有意思。 这是一个安全生产的信息管理平台,第一次听说,我的格局小了。 这是一个经典的 CPU 爆高问题,过往

记一次线上Redis内存占用过高、大Key问题的排查

问题背景 在一个风和日丽的下午,公司某项目现场运维同学反馈,生产环境3个Redis的Sentinel集群节点内存占用都很高,达到了17GB的内存占用量。 稍加思索,应该是某些Key的Value数据体量过大,占用了过多的内存空间,我们在使用Redis的过程中,单个Value或者单个集合中的元素应该保证

记一次 Redisson 线上问题 → ERR unknown command 'WAIT' 的排查与分析

开心一刻 昨晚和一个朋友聊天 我:处对象吗,咱俩试试? 朋友:我有对象 我:我不信,有对象不公开? 朋友:不好公开,我当的小三 问题背景 程序在生产环境稳定的跑着 直到有一天,公司执行组件漏洞扫描,有漏洞的 jar 要进行升级修复 然后我就按着扫描报告将有漏洞的 jar 修复到指定的版本 自己在开发

记一次 .NET 某电力系统 内存暴涨分析

一:背景 1. 讲故事 前些天有位朋友找到我,说他生产上的程序有内存暴涨情况,让我帮忙看下怎么回事,最简单粗暴的方法就是让朋友在内存暴涨的时候抓一个dump下来,看一看大概就知道咋回事了。 二:Windbg 分析 1. 到底是谁吃了内存 这个问题说的再多也不为过,一定要看清楚这个程序是如何个性化发展

记一次 .NET某账本软件 非托管泄露分析

一:背景 1. 讲故事 中秋国庆长假结束,哈哈,在老家拍了很多的短视频,有兴趣的可以上B站观看:https://space.bilibili.com/409524162 ,今天继续给大家分享各种奇奇怪怪的.NET生产事故,希望能帮助大家在未来的编程之路上少踩坑。 话不多说,这篇看一个.NET程序集泄

记一次 .NET 某企业OA后端服务 卡死分析

一:背景 1.讲故事 前段时间有位朋友微信找到我,说他生产机器上的 Console 服务看起来像是卡死了,也不生成日志,对方也收不到我的httpclient请求,不知道程序出现什么情况了,特来寻求帮助。 哈哈,一般来说卡死的情况在窗体程序(WinForm,WPF) 上特别多,在 Console,We

记一次 .NET 某拍摄监控软件 卡死分析

一:背景 1. 讲故事 今天本来想写一篇 非托管泄露 的生产事故分析,但想着昨天就上了一篇非托管文章,连着写也没什么意思,换个口味吧,刚好前些天有位朋友也找到我,说他们的拍摄监控软件卡死了,让我帮忙分析下为什么会卡死,听到这种软件,让我不禁想起了前些天 在程序员桌子上安装监控 的新闻,参考如下: 我

记一次 腾讯会议 的意外崩溃分析

一:背景 1. 讲故事 前段时间在用 腾讯会议 直播的时候,居然意外崩溃了,还好不是在训练营上课,不然又得重录了,崩完之后发现 腾讯会议 的 bugreport 组件会自动生成一个 minidump,截图如下: 作为一个.NET高级调试的技术博主,非 .NET 的程序也得要研究研究哈😄😄😄,有