Redisson初始
最近的自己,一直都在做些老年的技术,没有啥升级,自己也快麻木了,自己该怎么说,那必须行动起来啊!~来来,我们一起增长自己的内功
分布式锁的最强实现: Redisson
1.概念
在介绍之前,我们要知道这个
Redisson
是啥? 难道就是Redis
的son
?(我第一次就这么认为的哈哈!) 事实也的确如此–>看下面解释首先来点专业点解释:
Redisson
是In-momery data Grid
(建立在Redis
基础上的java
驻内存网格)(一种针对分布式的缓冲技术挺棒的!)通讯基于
Netty
,面向企业级开发提供一系列的分布式
java
常用对象,以及分布式服务等大白话:
a.就是特么封装许多分布式常用功能的分布式工具,
b.简化开发者开发,更专注于业务开发 ,而不是话大多时间的
Redis
上面 c.避免分布式造轮子
2.分布式锁
这玩意儿我也说下吧?就是并发业务的刚需;俗话说: 并发好不好,就看这把锁管的好不好!
有啥example
呢? (有啊!骚等~ ) 我们就以Redis
来写一个简单的分布式锁:~
编写一个最简单的
Redis
分布式锁: 采用SpringDataRedis
的RedisTemplate
setIfAbsent
:setNx
+expire
2.1加锁操作/解锁操作
// 加锁
public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
}
// 解锁,防止删错别人的锁,以uuid为value校验是否自己的锁
public void unlock(String lockName, String uuid) {if(uuid.equals(redisTemplate.opsForValue().get(lockName)){ redisTemplate.opsForValue().del(lockName); }
}
// 结构
if(tryLock){// todo
}finally{unlock;
}
上面的代码完成锁操作,但是我们会发现无法保证原子性,一旦并发量高了,就要好家伙了!~
咋解决呢?咋保证原子性啊?我咋知道啊!(哈哈!我们往下读,自会柳暗花明)我们引入Redis
内置的语言:lua
,
2.2采用lua
脚本
我们使用
lua
的 ,将操作封装成一个lua
脚本,通过Redis
的eval
/evalsha
lua
脚本代码:
脚本名称:
lockDel.lua
if redis.call('get', KEYS[1]) == ARGV[1] then -- 执行删除操作return redis.call('del', KEYS[1]) else -- 不成功,返回0return 0
end
删除java
代码:
// 解锁脚本
DefaultRedisScript<Object> unlockScript = new DefaultRedisScript();
unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lockDel.lua")));// 执行lua脚本解锁
redisTemplate.execute(unlockScript, Collections.singletonList(keyName), value);
写到这儿,我们也添加了lua
,也能保证原子性了吧!但是你看看是不是少了啥?(哈哈,你心里是不是在说,我看出来个锤子啊!没毛病啊)
嗯嗯,那我提个醒—按照上面的思路:如果一个线程多次拿锁呢?好家伙是不是来问题了?怎么解决呢?(想没想到?都往下看吧!哈哈)
2.3可重入锁
怎么保证可重入?
首先: 我们应该明白可重入的核心:同一个线程多次获取同一把锁是许可的,不会产生死锁
嗯~道理没怎么懂,我们用
Synchronized
的偏向锁来理解其实现思路
synchronized
实现重入是在JVM
层面,java
对象头MARK word
中含有线程ID和计数器对线程做重入判断,从而避免每次的CAS
- 当一个线程访问同步块并获取锁时,会在对象头和栈帧的锁记录里存储偏向的线程
ID
- 这样后面此线程ID再进入和退出同步块时,就不用
CAS
来进行加锁和解锁啦- 只需要检测MARK WORD对象头里面是否存储指向当前线程的偏向锁
- 测试成功:线程获得锁
- 测试失败:再测试下
MARK WORD
偏向锁标志是否设置为1
- 0: 没有
CAS
竞争- 1:
CAS
将对象头偏向锁指向当前线程- 还有一个计数器:同个线程进入则自增1,离开再减1,直到为0释放
根据上面的思路:(我们对要实现的lua
脚本进行改造)
1.准备形参(执行的条件)
需要存储锁名称lockName
该锁线程ID
对应线程进入的次数: count
2.加锁(每次线程获取锁时,判断是否已经存在该锁)
不存在:设置hash的Key为线程ID,value=1设置expireTime(过期时间)返回获取锁,成功为true
存在:继续判断是否存在当前线程ID的hash key存在:线程key的value+1,重入次数加1(count++),设置expireTime不存在:返回加锁失败
3.解锁 —>每次线程来解锁时,判断是否已经存在该锁
存在:是否有该线程的ID的hash key,有则减1,没有返回解锁失败减1后:判断剩余count为不为0=0: 不再需要这把锁,执行del命令删除
1.存储结构:
Redis采用HASH结构
锁名称: lock_name1`
key: thread_id (唯一键,线程ID)
value
:
count` (计数器)
hset lock_name1 thread_id 1hget lock_name1
2.计数器加减
当同一个线程获取同一把锁时,我们需要对应线程的计数器count做加减
怎么判断一个Redis的key是否存在,
我们可以用exists/hexists判断一个hash的key是否存在,
hset lock_name1 thread_id 1
exists/exists lock_name1
hash的自增命令:
hincrby lock_name1 thread_id 1
3.解锁的判断
当一把锁不再被需要了,每解锁一次,count减1,直到为0,执行删除
最终的LUA
代码
加锁
lock.lua
local key = KEYS[1];
local threadId = ARGV[1];
local releaseTime = ARGV[2];-- lockname不存在
if(redis.call('exists', key) == 0) thenredis.call('hset', key, threadId, '1');redis.call('expire', key, releaseTime);return 1;
end;-- 当前线程已id存在
if(redis.call('hexists', key, threadId) == 1) thenredis.call('hincrby', key, threadId, '1');redis.call('expire', key, releaseTime);return 1;
end;
return 0;
解锁
unlock.lua
local key = KEYS[1];
local threadId = ARGV[1];-- lockname、threadId不存在
if (redis.call('hexists', key, threadId) == 0) thenreturn nil;
end;-- 计数器-1
local count = redis.call('hincrby', key, threadId, -1);-- 删除lock
if (count == 0) thenredis.call('del', key);return nil;
end;
RedisLock.java
: 实现分布式锁实现功能: 互斥,可重入,防死锁
/*** @description 原生redis实现分布式锁**/
@Getter
@Setter
public class RedisLock {private RedisTemplate redisTemplate;private DefaultRedisScript<Long> lockScript;private DefaultRedisScript<Object> unlockScript;public RedisLock(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;// 加载加锁的脚本lockScript = new DefaultRedisScript<>();this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));this.lockScript.setResultType(Long.class);// 加载释放锁的脚本unlockScript = new DefaultRedisScript<>();this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));}/*** 获取锁*/public String tryLock(String lockName, long releaseTime) {// 存入的线程信息的前缀String key = UUID.randomUUID().toString();// 执行脚本Long result = (Long) redisTemplate.execute(lockScript,Collections.singletonList(lockName),key + Thread.currentThread().getId(),releaseTime);if (result != null && result.intValue() == 1) {return key;} else {return null;}}/*** 解锁* @param lockName* @param key*/public void unlock(String lockName, String key) {redisTemplate.execute(unlockScript,Collections.singletonList(lockName),key + Thread.currentThread().getId());}
}
上面代码,已经实现了大部分的功能,对于一般的场景都是可以从容应对了,但是(哈哈哈,最怕但是了是不?)
针对特殊场景:
1.若A进程在获取到锁的时候,因为业务操作时间太长,锁释放了,但是业务还是在执行,此刻B 进程有可以正常拿到锁进行业务操作,这时候就会出现A,B进程共享资源的问题
2.若负责存储这个分布式锁的
Redis
宕机,儿此时这个锁正好处于锁住状态,这时就会造成锁死的状态
那对于这种情况时,我们需要采用锁续约
锁续约:延长锁的
ReleaseTime
直到完成业务期望结果,本质就是不断延长锁过期时间
但是,对于当中的处理续约,性能比如锁的最大等待时间,无效的锁申请,与以及失败重试机制等等我们写到猴年马月啊!~立即推=—>放弃哈哈哈!!
别别别~来这时候我们就要介绍我们今天的主角:Reidsson
!!(解决上面所有问题)
4.Redisson
分布式锁
兵马未动,粮草先行
我们看看咋使用啊~是不是
4.1引入依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version>
</dependency><!-- 另一种Spring集成starter,本章未使用 -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.13.6</version>
</dependency>
4.2配置
@Configuration
public class RedissionConfig {@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.password}")private String password;private int port = 6379;@Beanpublic RedissonClient getRedisson() {Config config = new Config();config.useSingleServer().setAddress("redis://" + redisHost + ":" + port).setPassword(password);config.setCodec(new JsonJacksonCodec());return Redisson.create(config);}
}
4.3启用分布式锁
简洁明了,只需要一个
RLock
,如下面的代码所示
@Resource
private RedissonClient redissonClient;RLock rLock = redissonClient.getLock(lockName);
try {boolean isLocked = rLock.tryLock(expireTime, TimeUnit.MILLISECONDS);if (isLocked) {// TODO}} catch (Exception e) {rLock.unlock();}
4.4RLock
还在更…
相关文章:
Redisson初始
最近的自己,一直都在做些老年的技术,没有啥升级,自己也快麻木了,自己该怎么说,那必须行动起来啊!~来来,我们一起增长自己的内功 分布式锁的最强实现: Redisson 1.概念 在介绍之前,我们要知道这个Redisson是啥? 难道就是Redis的son?(我第一次就这么认为的哈哈!) 事实也的确如…...

【华为OD题库-018】AI面板识别-Java
题目 Al识别到面板上有N(1<N≤100)个指示灯,灯大小一样,任意两个之间无重叠。由于AI识别误差,每次识别到的指示灯位置可能有差异,以4个坐标值描述Al识别的指示灯的大小和位置(左上角x1,y1,右下角x2.y2)。请输出先行…...

[概述] 点云滤波器
拓扑结构 点云是一种三维数据,有几种方法可以描述其空间结构,以利于展开搜索 https://blog.csdn.net/weixin_45824067/article/details/131317939 KD树 头文件:pcl/kdtree/kdtree_flann.h 函数:pcl::KdTreeFLANN 作用:…...
[笔记] 汉字判断
参考博客:如果判断一个字符是西文字符还是中文字符 结论: 汉字转数字后,会占两位字符位,两位都是负数。 参考下面代码 输入:你 输出:01 #include<bits/stdc.h> using namespace std; int main() {cha…...

Android开发笔记(三)—Activity篇
活动组件Activity 启动和结束生命周期启动模式信息传递Intent显式Intent隐式Intent 向下一个Activity发送数据向上一个Activity返回数据 附加信息利用资源文件配置字符串利用元数据传递配置信息给应用页面注册快捷方式 启动和结束 (1)从当前页面跳到新页…...

nodejs+vue+python+php在线购票系统的设计与实现-毕业设计
伴随着信息时代的到来,以及不断发展起来的微电子技术,这些都为在线购票带来了很好的发展条件。同时,在线购票的范围不断增大,这就需要有一种既能使用又能使用的、便于使用的、便于使用的系统来对其进行管理。在目前这种大环境下&a…...

基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码)
效果: 功能点: 1、四个档位 2、可点击加减切换档位 3、可以点击区域切换档位 4、可以滑动切换档位 目的: 给大家提供一些实现思路,找了一圈,一些文章基本不能直接用,错漏百出,代码还藏着掖…...

城市内涝解决方案:实时监测,提前预警,让城市更安全
城市内涝积水问题是指城市地区在短时间内遭遇强降雨后,地面积水过多,导致城市交通堵塞、居民生活不便、财产损失等问题。近年来,随着全球气候变化和城市化进程的加速,城市内涝积水问题越来越突出,成为城市发展中的一大…...

编译正点原子LINUXB报错make: arm-linux-gnueabihf-gcc:命令未找到
编译正点原子LINUX报错make: arm-linux-gnueabihf-gcc:命令未找到 1.报错内容2.解决办法3./bin/sh: 1: lzop: not found4.编译成功 1.报错内容 make: arm-linux-gnueabihf-gcc:命令未找到CHK include/config/kernel.releaseCHK include/generat…...

工地现场智慧管理信息化解决方案 智慧工地源码
智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术,以PC端,移动端,设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等…...

Javaweb之HTML,CSS的详细解析
2. HTML & CSS 1). 什么是HTML ? HTML: HyperText Markup Language,超文本标记语言。 超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容。 标记语言:由标签构成的语言…...

基于python+django+vue开发的酒店预订管理系统 - 毕业设计 - 课程设计
文章目录 源码下载地址项目介绍项目功能界面预览项目备注毕设定制,咨询 源码下载地址 点击这里下载源码 项目介绍 该系统是基于pythondjango开发的酒店预定管理系统。适用场景:大学生、课程作业、毕业设计。学习过程中,如遇问题可在github…...

使用vscode实现远程开发,并通过内网穿透在公网环境下远程连接
文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…...
ArrayList集合2
ArrayList集合的一些方法 ⑥chear()从列表中移除所有元素 ⑦.isEmpty()判断列表中是否包含元素,不包含返回true,否则返回false public class Test{public static void main(String[] args){Arraylist<String> list new Arraylist<String>()…...

vue+asp.net Web api前后端分离项目发布部署
一、前后端项目介绍 1.前端项目是使用vue脚手架进行创建的。 脚手架版本:vue/cli 5.0.8 编译器版本:vs code 1.82.2 2.后端是一个asp.net Core Web API 项目 后端框架版本:.NET 6.0 编译器版本:vs 2022 二、发布部署步骤 第…...

iOS App Store上传项目报错 缺少隐私政策网址(URL)解决方法
iOS App Store上传项目报错 缺少隐私政策网址(URL)解决方法 一、问题如下图所示: 二、解决办法:使用Google浏览器(翻译成中文)直接打开该网址 https://www.freeprivacypolicy.com/free-privacy-policy-generator.php 按…...

如何使用Ruby 多线程爬取数据
现在比较主流的爬虫应该是用python,之前也写了很多关于python的文章。今天在这里我们主要说说ruby。我觉得ruby也是ok的,我试试看写了一个爬虫的小程序,并作出相应的解析。 Ruby中实现网页抓取,一般用的是mechanize,使…...

一文深入了解 CPU 的型号、代际架构与微架构
在 10 月 16 号的时候,Intel 正式发布了第 14 代的酷睿处理器。但还有很多同学看不懂这种发布会上发布的各种 CPU 参数。借着这个时机,给大家深入地讲讲 CPU 的型号规则、代际架构与微架构方面的知识。 CPU 在整个计算机硬件中、技术体系中都算是最最重…...

Java通过cellstyle属性设置Excel单元格常用样式全面总结
最近做了一个导出Excel的功能,导出是个常规导出,但是拿来模板一看,有一些单元格的样式设置,包括合并,背景色,字体等等,毕竟不是常用的东西,需要查阅资料完成,但是搜遍全网…...

如何查看WiFi密码
本文分享一下手机和电脑上如何查看已经连接过的WiFi的密码,然后好分享给他人。 手机上分享wifi密码很简单,步骤如下: 生成二维码,读取WiFi密码 1、首先,在“设置”中找到“无线网络”,点击需要查找密码的Wi…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...

深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...

【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...