Redis 线程控制 总结

news2024/10/28 15:00:40

前言


 相关系列

  • 《Redis & 目录》(持续更新)
  • 《Redis & 线程控制 & 源码》(学习过程/多有漏误/仅作参考/不再更新)
  • 《Redis & 线程控制 & 总结》(学习总结/最新最准/持续更新)
  • 《Redis & 线程控制 & 问题》(学习解答/持续更新)
     

 参考文献

  • 《Redis分布式锁》
     
     

概述


    Redisson基于Redis提供了多项适用于分布式环境的线程控制功能。Redisson的本质是一套由Redis官方提供的Java版API,其提供了包括线程控制工具在内的多项功能,支持在分布式环境中开箱即用。

    Redisson锁支持自动释放。Redisson锁既可以像常规锁定义一样被无限持有至手动释放,也支持在超出指定时间后自动释放,而如此设计的目的则是为了避免客户端断线而导致锁永远无法被解锁的情况发生。此外即使我们对锁进行的是无限加锁,Redisson也会为该锁设置30秒的默认存活时间,并在确保锁依然被正常持有的情况下为之定时延续,从而得以彻底避免死锁现象。
 
 

可重入锁


public void lock() {
    // ---- 获取(重入)锁。
    RLock rLock = redissonClient.getLock("lock");
    // ---- 无限加锁。
    rLock.lock();
    // ---- 有限加锁。
    // rLock.lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        rLock.unlock();
    }
}

    Redisson使用哈希类型来记录可重入锁信息。在Redisson的设计&实现中,其会使用哈希类型来记录可重入锁的数据信息,目的是为了将加锁线程标记/锁重入次数分别作为键/值好一次性保存。Redisson会为欲加锁可重入锁的线程生成UUID以作为唯一标记,以确保只有加锁线程才能执行重入/解锁。可重入锁的本质是独占锁,因此虽说哈希类型支持保存多个键/值对,但可重入锁的锁信息中永远只会存在单个键/值对。

在这里插入图片描述

    Redisson会为“无限加锁”的锁(不限于可重入锁)设置默认存活时间。为了避免网络断连而造成死锁,Redisson会为无限加锁的锁设置30秒的默认存活时间。如此一来即使某客户端与Redis中途断连而导致加锁线程未能手动解锁,Redis也能保证在最迟30秒后将该锁自动删除/解锁,从而确保不会出现其它线程永远无法加锁的死锁情况。

    Redisson会为“无限加锁”的锁(不限于可重入锁)续命/续约。Redisson为无限加锁的锁设置默认存活时间这一点带来的最大问题是:如果加锁线程持有锁的时间超过30秒的默认存活时间,那么不就可能出现其它成功加锁相同锁的情况吗?必然此时该锁已被Redis自动删除/解锁了。事实上正常情况下也确实会如此,因此Redisson还支持为无限加锁的锁进行续命/续约来避免该问题。所谓锁续命/续约是指Redisson会为成功无限加锁的锁同步创建负责监控的Watch Dog @ 看门狗线程,当看门狗线程以10秒的频率监控发现锁依然被正常持有时,其便会将之剩余存活时间重置为初始的30秒,直至锁被手动解锁或因为网络断连而无法更新为止。
 
 

可重入公平锁


public void fairLock() {
    // ---- 获取(重入)公平锁。
    RLock rLock = redissonClient.getFairLock("fair:lock");
    // ---- 无限加锁。
    rLock.lock();
    // ---- 有限加锁。
    // rLock.lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        rLock.unlock();
    }
}

    可重入公平锁是可重入锁的公平类型。可重入锁是不公平的,即线程不会按对可重入锁的访问顺序来依次完成加锁,但可重入公平锁却借助FIFO @ 先入先出队列实现了这一点,因为“队列的先入先出特性”及“只允许头部线程加锁的规则”能够保证线程必然会按访问顺序来依次加锁可重入锁。

在这里插入图片描述
 
 

读写锁


public void readLock() {
    RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock("read:write:lock");
    // ---- 无限加锁。
    rReadWriteLock.readLock().lock();
    // ---- 有限加锁。
    // rReadWriteLock.readLock().lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        rReadWriteLock.readLock().unlock();
    }
}

public void writeLock() {
    RReadWriteLock rReadWriteLock = redissonClient.getReadWriteLock("read:write:lock");
    rReadWriteLock.writeLock().lock();
    // ---- 有限加锁。
    // rReadWriteLock.writeLock().lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        rReadWriteLock.writeLock().unlock();
    }
}

    Redisson会额外记录读写锁当前被持有的锁类型。为了能够知晓读写锁被持有的具体类型,Redisson会将之以mode为键记录在读写锁的哈希中,而代表读/写锁的值则分别为read/write。此外由于读锁是共享锁,会出现被多个线程同时持有的情况。因此与上文所述独占锁仅有单个键/值对的哈希不同,读写锁在读锁被持有时其哈希中可能会存在多个键/值对。

在这里插入图片描述
在这里插入图片描述
    Redisson会独立记录每个读线程的加锁时长。由于读锁支持被多线程同时持有,并且多线程持有读锁的时长也并不一定相同,因此除了会将读线程的UUID存入哈希外,Redisson还会额外以{读写锁键}:读线程UUID:rwlock_timeout:1的格式生成键来记录读线程的具体加锁时长。不过需要注意的是:读线程的加锁时长并不是以值的形式存在的,而是会以键/值对存活时间的形式存在,因此该键/值对的失效就同步意味着该线程对读锁的持有已自动到期。 而如果读线程是无限加锁,那么Redisson便会通过看门狗线程来定时重置其剩余存活时间至30秒…可一个看门狗线程真的能够同时应对这么多无限加锁的读线程吗?这个问题但从数据结构上我并无法获得答案…源码?以后再说吧😁。

在这里插入图片描述
 
 

红锁


public void redLock() {
    RLock rLock1 = redissonClient.getLock("lock:1");
    RLock rLock2 = redissonClient.getLock("lock:2");
    RLock rLock3 = redissonClient.getLock("lock:3");
    // ---- 使用红锁同时加三个锁。
    RLock redLock = redissonClient.getRedLock(rLock1, rLock2, rLock3);
    // ---- 无限加锁。
    redLock.lock();
    // ---- 有限加锁。
    // redLock.lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        redLock.unlock();
    }
}

    红锁的本质是使用多个锁来保护单个资源。我们很容易理解的一点是:锁信息是存在丢失的可能,因为持久化机制/主从同步都无法保证数据完全不丢失。而由于非法解锁可能导致程序出现并发/异常等情况,因此Redisson便设计&提供了红锁来避免这一点。红锁的本质是使用多个锁来保护单个资源,如此一来即使少数锁的信息因为Redis实例的宕机而丢失,其它依然存在的锁信息也一样可以维持锁功能的的正常使用。

在这里插入图片描述

    红锁只在集群中使用才有意义。在单机/主从部署的Redis中使用红锁其实是没有太大意义的,因为无论你使用了多少锁来组成红锁,这些锁的信息也都只会被统一保存在单个Redis实例/主机中,因此锁信息一旦发生丢失往往也是全局性的。但在集群中情况就完全不一样了,由于这些组成红锁的锁会被分散到不同的主节点中保存信息,因此锁信息丢失也仅限于宕机主节点所拥有的部分。

在这里插入图片描述

    红锁已被淘汰。红锁在较新版本的Redisson中已经开始过时,原因正如上文所说是其仅在Redis集群中才有实用意义。而由于现实情况中绝大多数公司的业务体量都无法达到需要搭建Redis集群的程度,因此为了增强红锁的实用范围Redisson便对红锁的概念/实现进行了迭代。在较新的Redisson中,红锁已从单一的锁类型转变为通用的锁特性,即所有的Redis锁实现都默认携带有红锁功能,因此所谓淘汰是指作为单一锁类型的红锁被淘汰。红锁特性的作用表现在当线程试图进行加/解任意类型的锁时,如果要操作的目标Redis实例存在任意形式(主从/集群)的从机,那么其只有当主机中的锁信息被成功同步至从机后才会返回加/解锁成功…该设计变动带来的好处具体如下:

  • 多锁变为单锁,节省了锁信息的内存开销;
  • 锁信息从集群多地保存变为主从多地保存,虽然安全性整体有所降低,但亦保证了足够的体量;
  • 仅集群可用变为主从可用,提升了实用范围;
  • 单一锁类型转为通用锁特性,提升了使用范围。
     
     

联锁


public void multiLock() {
    // ---- 获取三个锁,这三个锁分别用于保护不同的资源。
    RLock rLock1 = redissonClient.getLock("lock:1");
    RLock rLock2 = redissonClient.getLock("lock:2");
    RLock rLock3 = redissonClient.getLock("lock:3");
    // ---- 使用联锁同时加三个锁。
    RLock multiLock = redissonClient.getMultiLock(rLock1, rLock2, rLock3);
    // ---- 无限加锁。
    multiLock.lock();
    // ---- 有限加锁。
    // multiLock.lock(30L, TimeUnit.SECONDS);
    try {
        // ---- 逻辑操作。
    } finally {
        multiLock.unlock();
    }
}

    联锁用于为多锁加锁提供“伪”原子性保证。所谓“伪”原子性是指在多锁加锁会连续执行,中间不会出现有其他Redis指令插队的情况。那为什么说是“伪”原子性呢?这是因为原子性存在“一同成功/失败”的概念规则,但上述任意锁的失败既不会造成回滚,也不后影响后续锁的加锁,因此便被成为“伪”原子性。联锁是一项非常实用的功能,因为多锁加锁是很容易因为顺序原因而出现死锁问题的…例如下文代码所示,而联锁的“伪原子性”则很好的避免了这点。

public void method1() {
    RLock rLock1 = redissonClient.getLock("lock:1");
    rLock1.lock();
    RLock rLock2 = redissonClient.getLock("lock:2");
    rLock2.lock();
    try {
        // ---- 逻辑操作。
    } finally {
        rLock2.unlock();
        rLock1.unlock();
    }
}
// ---- 两个方法都要加锁1/2,但两者的顺序却不一样。这就可能出现线程A/B分别成功
// 加锁锁1/2然后相互等待锁2/1的死锁情况。

public void method2() {
    RLock rLock2 = redissonClient.getLock("lock:2");
    rLock2.lock();
    RLock rLock1 = redissonClient.getLock("lock:1");
    rLock1.lock();
    try {
        // ---- 逻辑操作。
    } finally {
        rLock1.unlock();
        rLock2.unlock();
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2225502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何指定 Maven 的 JDK 版本?

maven 路径:/data/maven/ jdk 路径:/data/jdk_1.8 1、修改 mvn 可执行文件并指定 JDK 版本 vim /data/maven/bin/mvn # 在开头新增即可... # zhurs add JAVA_HOME PATH JAVA_HOME/data/jdk_1.8 ...保存退出即可! 为什么在此处新增&#x…

AIGC时代的数据盛宴:R语言引领数据分析新风尚

文章目录 一、AIGC时代的挑战与R语言的机遇二、R语言在AIGC时代的数据预处理与清洗三、R语言在AIGC时代的统计分析四、R语言在AIGC时代的数据可视化五、R语言在AIGC时代的自动化报告生成六、R语言在AIGC时代的优势与未来发展《R语言统计分析与可视化从入门到精通》亮点内容简介…

软工毕设开题建议

文章目录 🚩 1 前言1.1 选题注意事项1.1.1 难度怎么把控?1.1.2 题目名称怎么取? 1.2 开题选题推荐1.2.1 起因1.2.2 核心- 如何避坑(重中之重)1.2.3 怎么办呢? 🚩2 选题概览🚩 3 项目概览题目1 : 深度学习社…

spring-boot(4)

1.VueRouter安装与使用 2.状态管理VueX 3.Mock 如果后端没写好,可以通过这个来随机返回前端需要的后端数据,只不过是随机的。 vue: mounted:function (){console.log("-------------------------------------Hello");axios.get("http://…

华为原生鸿蒙操作系统的发布有何重大意义和影响:

#1024程序员节 | 征文# 一、华为原生鸿蒙操作系统的发布对中国的意义可以从多个层面进行分析: 1. 技术自主创新 鸿蒙操作系统的推出标志着中国在操作系统领域的自主创新能力的提升。过去,中国在高端操作系统方面依赖于外国技术,鸿蒙的发布…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-24目录1. Optimizing Preference Alignment with Differentiable NDCG Ranking摘要研究背景问题与挑战如何解决创新点算法模型算…

Flutter登录界面使用主题

Now, let’s use the theme we initially created in our main function for a simple login screen: 现在,让我们使用最初在主函数中创建的主题来制作一个简单的登录屏幕: Create a Login Screen Widget: Inside the main.dartfile, create a new wid…

【Qt】常用控件:按钮类控件

思维导图: 一、Push Button 我们可以使用 QPushButton 表示一个按钮,这也是当前我们最熟悉的一个控件。QPushButton继承于QAbstractButton。这个类是一个抽象类,是按钮的父类。 1.1 常用的属性 属性说明text按钮中的文本icon按钮中的图标ic…

「C/C++」C++ STL容器库 之 std::list 双向链表容器

✨博客主页何曾参静谧的博客📌文章专栏「C/C」C/C程序设计📚全部专栏「VS」Visual Studio「C/C」C/C程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「DSA」数据结构与算法「UG/NX」NX二次开发「QT」QT5程序设计「File」数据文件格式「PK」Parasoli…

【STM32】单片机ADC原理详解及应用编程

本篇文章主要详细讲述单片机的ADC原理和编程应用,希望我的分享对你有所帮助! 目录 一、STM32ADC概述 1、ADC(Analog-to-Digital Converter,模数转换器) 2、STM32工作原理 二、STM32ADC编程实战 (一&am…

小程序中设置可拖动区域

官方说明文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/component/movable-area.htmlhttps://developers.weixin.qq.com/miniprogram/dev/component/movable-view.html demo&#xff1a;浮动控件上下移动交互 .wxmx <movable-area><!-- y"…

python之多任务爬虫——线程、进程、协程的介绍与使用(16)

文章目录 1、什么是多任务?1.1 进程和线程的概念1.2 多线程与多进程的区别1.3 并发和并行2、python中的全局解释器锁3、多线程执行机制4、python中实现多线程(threading模块)4.1 模块介绍4.2 模块的使用5、python实现多进行程(Multiprocessing模块)5.1 导入模块5.2 模块的…

多层感知机的从零实现与softmax的从零实现(真·0000零基础)

今天再读zh.d2l书&#xff08;4.2. 多层感知机的从零开始实现 — 动手学深度学习 2.0.0 documentation&#xff09;&#xff0c; 看了关于多层感知机的从零实现与softmax的从零实现 目录 mlp从零实现&#xff0c; 点击“paddle”的代码 点击“torch”的代码 训练 参数解…

k8s部署使用有状态服务statefulset部署eureka集群,需登录认证

一、构建eureka集群镜像 1、编写dockerfile文件&#xff0c;此处基础镜像为arm版本&#xff0c;eureka目录中文件内容&#xff1a;application-dev.yml、Dockerfile、eureka-server-1.0-SNAPSHOT.jar(添加登录认证模块&#xff0c;文章最后附上下载连接) FROM mdsol/java8-j…

深入探索:深度学习在时间序列预测中的强大应用与实现

引言&#xff1a; 时间序列分析是数据科学和机器学习中一个重要的研究领域&#xff0c;广泛应用于金融市场、天气预报、能源管理、交通预测、健康监控等多个领域。时间序列数据具有顺序相关性&#xff0c;通常展示出时间上较强的依赖性&#xff0c;因此简单的传统回归模型往往…

基于SpringBoot的“社区维修平台”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“社区维修平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 管理员登录页面 住户管理页面 社区公关管理页面 维…

【JVM】——JVM运行机制、类加载机制、内存划分

阿华代码&#xff0c;不是逆风&#xff0c;就是我疯 你们的点赞收藏是我前进最大的动力&#xff01;&#xff01; 希望本文内容能够帮助到你&#xff01;&#xff01; 目录 一&#xff1a;JVM引入 1&#xff1a;编程语言 2&#xff1a;JAVA运行机制 二&#xff1a;JVM中内存…

EVADC模块多路触发导致AD值波动

前言&#xff1a;最近开发中遇到一个问题&#xff0c;某一路ADC通道采集的AD值波动比较厉害&#xff0c;达到9个hex值波动&#xff0c;对此进行了分析排查...... 1 排除硬件因素 用示波器对电路图上该ADC通道的测试点进行电压测量&#xff0c;发现电压比较稳定&#xff0c;换算…

【计算机网络三】一篇文章详解TCP/IP四层协议簇

目录 TCP/IP四层协议簇 1.应用层 DNS NAT NAPT HTTP/HTTPS 2.传输层 TCP协议 UDP协议 3.网络层 IP协议 4.数据链路层 以太网 ARP协议 TCP/IP四层协议簇 TCP/IP四层协议簇是目前世界上最流行的网络协议分层方式&#xff0c;本篇文章我将带大家从上到下详解四层协议…

开放式蓝牙耳机哪个品牌好用?爆款开放式耳机推荐!

在当今蓝牙耳机市场中&#xff0c;开放式蓝牙耳机以其独特的设计和舒适的佩戴体验&#xff0c;逐渐成为众多消费者的新宠。然而&#xff0c;面对众多品牌和款式的开放式蓝牙耳机&#xff0c;消费者往往陷入选择的困境。究竟哪个品牌的开放式蓝牙耳机真正好用&#xff1f;其性能…