《伸手系列》之分布式锁Redssion入门和源码解析
Redisson简介
Javaer都知道Jedis,Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持。Redission也是Redis的客户端,相比于Jedis功能简单。Jedis简单使用阻塞的I/O和redis交互,Redission通过Netty支持非阻塞I/O。Jedis最新版本2.9.0是2016年的快3年了没有更新,而Redission最新版本是2018.10月更新。
Redission封装了锁的实现,其继承了java.util.concurrent.locks.Lock的接口,让我们像操作我们的本地Lock一样去操作Redission的Lock。
下面直接上干货
使用样例
@GetMapping("/testLock")public String lock(){RLock lock = redissonClient.getLock("anyLock");lock.lock();try {System.out.println(lock);Thread.sleep(TimeUnit.SECONDS.toMillis(30));} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally {lock.unlock();}return "ok" ;}
源码分析
- 获取锁
调用getLock()方法后实际返回一个RedissonLock对象
- 加锁
在RedissonLock对象的lock()方法主要调用tryAcquire()方法,由于leaseTime == -1,于是走tryLockInnerAsync()方法
- 加锁细节
结合上面的参数声明,我们可以知道,这里KEYS[1]就是getName(),ARGV[2]是getLockName(threadId),假设前面获取锁时传的name是“anyLock”,假设调用的线程ID是Thread-1,假设成员变量UUID类型的id是85b196ce-e6f2-42ff-b3d7-6615b6748b5d:65那么KEYS[1]=anyLock,ARGV[2]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1 ,因此,这段脚本的意思是1、判断有没有一个叫“anyLock”的key2、如果没有,则在其下设置一个字段为“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”,值为“1”的键值对 ,并设置它的过期时间3、如果存在,则进一步判断“85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1”是否存在,若存在,则其值加1,并重新设置过期时间4、返回“anyLock”的生存时间(毫秒)
- 加锁redis结构
这里用的数据结构是hash,hash的结构是: key 字段1 值1 字段2 值2 。。。用在锁这个场景下,key就表示锁的名称,也可以理解为临界资源,字段就表示当前获得锁的线程所有竞争这把锁的线程都要判断在这个key下有没有自己线程的字段,如果没有则不能获得锁,如果有,则相当于重入,字段值加1(次数)
- 解锁
我们还是假设name=anyLock,假设线程ID是Thread-1,同理,我们可以知道KEYS[1]是getName(),即KEYS[1]=anyLock,KEYS[2]是getChannelName(),即KEYS[2]=redisson_lock__channel:{anyLock},ARGV[1]是LockPubSub.unlockMessage,即ARGV[1]=0,ARGV[2]是生存时间,ARGV[3]是getLockName(threadId),即ARGV[3]=85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1,因此,上面脚本的意思是:1、判断是否存在一个叫“anyLock”的key2、如果不存在,向Channel中广播一条消息,广播的内容是0,并返回1。3、如果存在,进一步判断字段85b196ce-e6f2-42ff-b3d7-6615b6748b5d:Thread-1是否存在。4、若字段不存在,返回空,若字段存在,则字段值减1,5、若减完以后,字段值仍大于0,则返回0。6、减完后,若字段值小于或等于0,则广播一条消息,广播内容是0,并返回1;可以猜测,广播0表示资源可用,即通知那些等待获取锁的线程现在可以获得锁了
- 等待
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {long threadId = Thread.currentThread().getId();Long ttl = tryAcquire(leaseTime, unit, threadId);// lock acquiredif (ttl == null) {return;}RFuture<RedissonLockEntry> future = subscribe(threadId);if (interruptibly) {commandExecutor.syncSubscriptionInterrupted(future);} else {commandExecutor.syncSubscription(future);}try {while (true) {ttl = tryAcquire(leaseTime, unit, threadId);// lock acquiredif (ttl == null) {break;}// waiting for messageif (ttl >= 0) {try {future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} catch (InterruptedException e) {if (interruptibly) {throw e;}future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);}} else {if (interruptibly) {future.getNow().getLatch().acquire();} else {future.getNow().getLatch().acquireUninterruptibly();}}}} finally {unsubscribe(future, threadId);}
// get(lockAsync(leaseTime, unit));}
这里会订阅Channel,当资源可用时可以及时知道,并抢占,防止无效的轮询而浪费资源当资源可用用的时候,循环去尝试获取锁,由于多个线程同时去竞争资源,所以这里用了信号量,对于同一个资源只允许一个线程获得锁,其它的线程阻塞
- 总结
关注Github:1/2极客
关注博客:御前提笔小书童
关注网站:HuMingfeng
关注公众号:开发者的花花世界
《伸手系列》之分布式锁Redssion入门和源码解析相关推荐
- 分布式锁简单入门以及三种实现方式介绍(滴滴)
很多小伙伴在学习Java的时候,总是感觉Java多线程在实际的业务中很少使用,以至于不会花太多的时间去学习,技术债不断累积!等到了一定程度的时候对于与Java多线程相关的东西就很难理解,今天需要探讨的 ...
- Redisson分布式锁快速入门教程
清明在家无事,并且因为上海疫情原因只能宅在家里,突然想到之前计划着写一篇Redisson的分布式锁快速入门教程,自己平常在工作中也只能简单会使用,所以文章可能写的比较简单,希望大佬勿喷.此文章也作为个 ...
- 分布式锁简单入门以及三种实现方式介绍
分布式锁简单入门以及三种实现方式介绍 2018年01月11日 21:16:28 徐刘根 阅读数:37912 标签: 分布式 分布式锁 高并发 更多 个人分类: 集群分布式 版权声明:本文为博主原创文章 ...
- Google Test(GTest)使用方法和源码解析——断言的使用方法和解析
在之前博文的基础上,我们将介绍部分断言的使用,同时穿插一些源码.(转载请指明出于breaksoftware的csdn博客) 断言(Assertions) 断言是GTest局部测试中最简单的使用方法,我 ...
- 解析并符号 读取dll_Spring IOC容器之XmlBeanFactory启动流程分析和源码解析
一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...
- Dubbo原理和源码解析之服务引用
github新增仓库 "dubbo-read"(点此查看),集合所有<Dubbo原理和源码解析>系列文章,后续将继续补充该系列,同时将针对Dubbo所做的功能扩展也进行 ...
- Google Test(GTest)使用方法和源码解析——模板类测试技术分析和应用
写C++难免会遇到模板问题,如果要针对一个模板类进行测试,似乎之前博文中介绍的方式只能傻乎乎的一个一个特化类型后再进行测试.其实GTest提供了两种测试模板类的方法,本文我们将介绍方法的使用,并分析其 ...
- Google Test(GTest)使用方法和源码解析——参数自动填充技术分析和应用
在我们设计测试用例时,我们需要考虑很多场景.每个场景都可能要细致地考虑到到各个参数的选择.比如我们希望使用函数IsPrime检测10000以内字的数字,难道我们要写一万行代码么?(转载请指明出于bre ...
- Google Test(GTest)使用方法和源码解析——私有属性代码测试技术分析
有些时候,我们不仅要测试类暴露出来的公有方法,还要测试其受保护的或者私有方法.GTest测试框架提供了一种方法,让我们可以测试类的私有方法.但是这是一种侵入式的,会破坏原来代码的结构,所以我觉得还是谨 ...
- Google Test(GTest)使用方法和源码解析——预处理技术分析和应用
预处理 在<Google Test(GTest)使用方法和源码解析--概况>最后一部分,我们介绍了GTest的预处理特性.现在我们就详细介绍该特性的使用和相关源码.(转载请指明出于brea ...
最新文章
- Java的Redis连接池代码性能不错
- Redis初学16:主从复制
- 正则表达式的基本入门
- 四种参数传递的形式——URL,超链接,js,form表单
- 全球与中国文件夹架市场研究与商业模式创新分析报告2022-2028年
- sql两个表查不同数据_产品操作MySQL第6篇 – 数据过滤-WHERE子句
- WINCE Driver 心得总结
- (转)基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式...
- Kali Linux 秘籍 第一章 安装和启动Kali
- LeetCode-Clone Graph-克隆无向图
- python怎么读取excel-python怎么从excel中读取数据?
- Hibernate一对多双向关联
- linux fastboot 工具下载,FastBoot刷机工具
- Java Restful风格-Jersey RESTful 框架入门
- python画正切函数_在matplotlib中绘制tan
- 信号-失真噪声比 (SNDR) 无杂散动态范围 (SFDR)
- 《软件工程——实践者的研究方法》重难点复习笔记(第八章——理解需求)
- win10设置pin一直转圈_Win10 自带手机投屏功能,80% 的人居然都不知道
- iOS闪退问题,避免闪退看我就足够了, try catch等方法
- php动态链入,利用php的动态链接,增加搜索引擎的蜘蛛爬行速度,规则嵌入ElasticSearch泛目录...