Redisson中的“琐事”

news2024/11/15 21:57:43

文章目录

    • 前言
    • 锁分类
    • Redisson
      • 可重入锁(Reentrant Lock)
      • 公平锁(Fair Lock)
      • 联锁(MultiLock)
      • 红锁(RedLock)
      • 读写锁(ReadWriteLock)
      • 信号量(Semaphore)
      • 闭锁(CountDownLatch)

前言

在应用开发中,特别是web工程开发,通常都是并发编程,不是多进程就是多线程。这种场景下极易出现线程并发性安全问题,此时不得不使用锁来解决问题。在多线程高并发场景下,为了保证资源的线程安全问题,jdk为我们提供了synchronized关键字和ReentrantLock可重入锁,但是它们只能保证一个工程内的线程安全。在分布式集群、微服务、云原生横行的当下,如何保证不同进程、不同服务、不同机器的线程安全问题,jdk并没有给我们提供既有的解决方案。此时,我们就必须借助于相关技术手动实现了。目前主流的实现有以下方式:

  1. 基于mysql关系型实现
  2. 基于redis非关系型数据实现
  3. 基于zookeeper/etcd实现

在这里插入图片描述

锁分类

悲观锁:具有强烈的独占和排他特性,在整个数据处理过程中,将数据处于锁定状态。适合于写比较多,会阻塞读操作。
乐观锁:采取了更加宽松的加锁机制,大多是基于数据版本( Version )及时间戳来实现。。适合于读比较多,不会阻塞读

独占锁、互斥锁、排他锁:保证在任一时刻,只能被一个线程独占排他持有。synchronized、ReentrantLock
共享锁:可同时被多个线程共享持有。CountDownLatch倒计数器、Semaphore信号量

可重入锁:又名递归锁。同一个线程在外层方法获取锁的时候,在进入内层方法时会自动获取锁。
不可重入锁

公平锁:有优先级的锁,先来先得,谁先申请锁就先获取到锁
非公平锁:无优先级的锁,后来者也有机会先获取到锁

自旋锁:当线程尝试获取锁失败时(锁已经被其它线程占用了),无限循环重试尝试获取锁
阻塞锁:当线程尝试获取锁失败时,线程进入阻塞状态,直到接收信号后被唤醒。在竞争激烈情况下,性能较高

读锁:共享锁
写锁:独占排他锁

偏向锁:一直被一个线程所访问,那么该线程会自动获取锁
轻量级锁(CAS):当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,
其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁:当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候(10次),
还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。
以上其实是synchronized的锁升级过程

表级锁:对整张表加锁,加锁快开销小,不会出现死锁,但并发度低,会增加锁冲突的概率
行级锁:是mysql粒度最小的锁,只针对操作行,可大大减少锁冲突概率,并发度高,但加锁慢,开销大,会出现死锁

Redisson

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

在这里插入图片描述

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

可重入锁(Reentrant Lock)

基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。

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

RLock对象完全符合Java的Lock规范。也就是说只有拥有锁的进程才能解锁,其他进程解锁则会抛出IllegalMonitorStateException错误。

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

RLock lock = redisson.getLock("anyLock");
// 最常见的使用方法
lock.lock();

// 加锁以后10秒钟自动解锁
// 无需调用unlock方法手动解锁
lock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

1、引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.11.2</version>
</dependency>

2、添加配置

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        // 可以用"rediss://"来启用SSL连接
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        return Redisson.create(config);
    }
}

3、代码中使用

@Autowired
private RedissonClient redissonClient;

public void checkAndLock() {
    // 加锁,获取锁失败重试
    RLock lock = this.redissonClient.getLock("lock");
    lock.lock();

    // todo 一些处理业务

    // 释放锁
    lock.unlock();
}

公平锁(Fair Lock)

基于Redis的Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。

可重入锁拼的是运气,先到的请求不一定会先抢到锁
公平锁拼的是网速和手速,内部会有一个队列来保证顺序

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
fairLock.unlock();

联锁(MultiLock)

基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();

红锁(RedLock)

基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

读写锁(ReadWriteLock)

基于Redis的Redisson分布式可重入读写锁RReadWriteLock Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。

分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();

// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();
并发操作是否可以并发
写 和 写不能并发
读 和 写不能并发
读 和 读可以并发

信号量(Semaphore)

基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.trySetPermits(3);
//尝试获取许可,如果成功就会继续执行,否则就会被阻塞
semaphore.acquire();
//释放许可供其他线程使用,之前被阻塞的线程会被唤醒继续执行
semaphore.release();

Semaphore一般用于流量的控制,特别是公共资源有限的应用场景。例如数据库的连接,假设数据库的连接数上线为10个,多个线程并发操作数据库可以使用Semaphore来控制并发操作数据库的线程个数最多为10个。

例如商场的停车场只有三个车位,外边有十辆车需要进来,这时要保证同一时刻停车场只能有三辆车进来,有空车位了才能让别的车驶进来。

闭锁(CountDownLatch)

基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();

// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

JUC中的CountDownLatch作用只能局限在单个JVM中,一旦集群部署的话就无法保证了

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

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

相关文章

【C++】左值、右值、语义移动和完美转发

右值引入的目的是为了对象移动&#xff1a; 因为在很多情况下&#xff0c;对象拷贝会经常发生&#xff0c;但是很多对象在拷贝后就直接被销毁了。这对性能是一个很大损耗。在重新分配内存的时候&#xff0c;从旧的内存将元素拷贝到新的内存中是不必要的。更好的方法是移动元素。…

论文投稿指南——中文核心期刊推荐(天文、测绘学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

使用Kalibr问题汇总:ModuleNotFoundError: No module named ‘wx‘

问题1&#xff1a; 报错&#xff1a;/kalibr_ws/src/Kalibr/Schweizer-Messer/sm_python/python/sm/PlotCollection.py", line 4, in import wx ModuleNotFoundError: No module named ‘wx’ 解决&#xff1a; sudo apt-get install python3-wxgtk4.0问题2&#xff1…

MySQL补齐函数LPAD和RPAD之SQLite解决方案

工作中经常需要对数据进行清洗&#xff0c;并对个别字段进行格式化处理&#xff0c;像 字符串左右补齐。MySQL数据库自带有LPAD()、RPAD()&#xff0c;而SQLite数据库没有的相应函数&#xff0c;需要自己转换。 目录 1、MySQL数据库 1.1、MySQL左右补全函数 1.2、实践验证 …

阶段性回顾(5)与一些题目实例(数组合并,有序判断,删除元素,进制问题等)

tips 1. 内存栈区的使用习惯是先使用高地址&#xff0c;再使用低地址。并且你还要清楚&#xff1a;随着数组下标的增大&#xff0c;其元素的地址也是在不断变高&#xff1b;对于一个占多个内存单元的变量进行取地址&#xff0c;取出来的是其所占内存空间最低地址的内存单元的地…

Python内存机制 -- = 赋值操作

Python内存机制 python的万物皆对象可不只是说说而已。 1. 预备知识&#xff1a; id()&#xff1a;可以将id()理解为C语言中的*&#xff0c;其返回当前对象在内存中的地址。 int p id(object) # id函数返回对象object在其生命周期内位于内存中的地址&#xff0c;id函数的参数…

Python数据分析案例18——化学分子数据模型(机器学习分类问题全流程)

1. 引言 1.1设计背景 对分子进行分类&#xff0c;对于筛选特定疾病的候选药物是至关重要的。传统的机器学习算法可以对分子进行分类&#xff0c;但是分子不能直接作为机器学习模型的输入&#xff0c;需要进行大量的实验从分子中得到一系列的分子特性。将分子特征使用数字化进…

47-Jenkins-终止构建并设置构建结果

终止构建并设置构建结果前言获取构建结果终止构建并设置构建状态权限问题解决前言 本篇来学习Jenkins终止构建的方法&#xff0c;使用场景&#xff1a;根据前一个构建状态&#xff0c;判断当前构建是否运行 获取构建结果 上次构建结果&#xff1a;currentBuild.getPreviousB…

【LeetCode题目详解】(五)144.二叉树的前序遍历、94.二叉树的中序遍历、145.二叉树的后序遍历、104.二叉树的最大深度、110.平衡二叉树

目录 一、力扣第144题&#xff1a;二叉树的前序遍历 1.解题思路 2.解题代码 二、力扣第94题&#xff1a;二叉树的中序遍历 三、力扣第145题&#xff1a;二叉树的后序遍历 四、力扣第104题&#xff1a;二叉树的最大深度 1.解题思路 2.解题代码 五、力扣第110题&#xff1…

抖音直播间弹幕rpc学习

目标url 随便找个直播间即可。 https://live.douyin.com/198986091107 接口分析 首先并没有在xhr下找到对应的接口 因为采用了websocket来传输信息。切换到ws即可看到 消息下&#xff0c;可以看到16进制的数据在源源不断地增加。 那么我们只要找到反序列化后的数据&…

在wsl下开发T113的主线linux(4)-编译kernel

接下来编译kernel&#xff0c;编译过程可能会出现缺少命令的报错&#xff0c;大概是下面这几个 sudo apt update sudo apt install flex bison bc libncurses-dev 目前linux主线的最新版本并没有适配t113的相关外设驱动&#xff0c;虽然能启动并串口打印&#xff0c;但其他的…

在单选按钮上实现双击效果

如果想让你的用户体验更加极致&#xff0c;可以考虑在对话框上的单选按钮上实现双击效果&#xff0c;以此作为”选中 确定”&#xff08;或者是选中 下一页&#xff0c;或者是选中 完成等&#xff09;的快捷方式。 看看我们需要怎么做 下面是一个对话框模板以及它对应的对…

3.服务注册和远程调用-Nacos[作为注册中心]

1.SpringCloud Alibaba-Nacos[作为注册中心] Nacos 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现、配置管理和服务管理 平台。他是使用 java 编写。需要依赖 java 环境 Nacos 文档地址: https://nacos.io/zh-cn/docs/quick-start.html 下载 nacos-server https://…

HCIA作业整理(2022.12.29)

一、特殊的ip地址&#xff1a; 1.环回地址&#xff1a;127.0.0.1-127.255.255.254 2.受限广播地址&#xff1a;255.255.255.255 3.直接广播地址&#xff1a;主机位全为1、192.168.1.X/24 --- 192.168.1.255 4.网段&#xff1a;主机位全0 --- 192.168.1.X/24 --- 192.168.1…

STL剖析(一):体系结构概览

一.什么是STL? STL全称是Standard Template Library&#xff0c;它属于泛型编程的范畴&#xff08;泛型编程的代表性作品&#xff09;&#xff0c;泛型编程旨在编写独立于数据类型的代码&#xff0c;也就是说代码中的数据类型只有在编译的时候才会确定&#xff0c;否则为一个…

网络协议(一):基本概念、计算机之间的连接方式

网络协议系列文章 网络协议(一)&#xff1a;基本概念、计算机之间的连接方式 目录一、网络互联模型二、计算机之间的通信基础1、计算机之间的连接方式 - 网线直连2、计算机之间的连接方式 - 同轴电缆(Coaxial)3、计算机之间的连接方式 - 集线器(Hub)4、计算机之间的连接方式 -…

vue3 antd项目实战——Form表单的重置与重置【resetFields重置表单未生效(手写重置函数)】

vue3 antd项目实战——resetFields重置表单无效【手写重置函数重置表单数据】关于form表单的文章合集场景复现原因分析解决方案(手写清空函数)关于form表单的文章合集 文章内容文章链接Form表单提交和校验https://blog.csdn.net/XSL_HR/article/details/128495087?spm1001.20…

Hadoop高手之路5-MapRreduce

文章目录Hadoop高手之路5-MapReduce分布式计算框架一、MapReduce概述1.MapReduce核心思想2.MapReduce编程模型3.MapReduce编程实例——词频统计二、MapReduce的工作原理1. MapReduc的工作过程1) 分片、格式化数据源2) 执行MapTask3) 执行Shuffle4) 执行ReduceTask5) 写入文件2.…

5_虚拟机栈

虚拟机栈概述 由于跨平台性的设计&#xff0c;Java的指令都是根据栈来设计的。不同平台CPU架构不同&#xff0c;所以不能设计为基于寄存器的。 优点是跨平台&#xff0c;指令集小&#xff0c;编译器容易实现&#xff0c;缺点是性能下降&#xff0c;实现同样的功能需要更多的指…

QML教程(一)

目录 一、导入 二、对象声明 三、对象属性 1.声明对象属性 2.信号属性 3.方法属性 4.附加属性略 5.枚举属性 6.对象属性赋值 四、自定义对象 一、导入 模块导入 语法&#xff1a; import <ModuleIdentifier> [<Version.Number>] [as <Qualifier>…