上文,我们分析了redisson剑谱里面的前四式,参见【万剑归宗,redisson的百锁解构(上) 】,本文将继续解构后面的四个招式,对于这些招式的拆解,除了可以让我们对redisson知其然知其所以然,也可以帮助我们去理解jdk中本身就已经实现的那些锁。继续演练余下的几招,以指为剑,剑破苍穹。

【 招式四  读写锁 】

读写锁,在很多业务场景中,读多写少,多个客户端可以同时读,但是有人写的时候不能读,这个时候读写锁就是最佳选择了,自然是要分为读锁,写锁,而且肯定都是要单独加锁的,这其中就涉及了锁互斥的机制,读读,读写,写写,写读,这些情况都是怎么处理的呢?OK,我们从源码入手,见招拆招。

【 读锁&加锁】

读锁的加锁逻辑,也是在lua脚本中的,这段脚本也不长,首先呢他是根据

锁key的hash结构中去hget一个mode的值,这个mode的值要么是读read,要么是写write,我们第一次来加读锁,那肯定就是不存在的,就进入分支进行加锁,加锁的逻辑很简单,把mode设置为read,设置37f75873-494a-439c-a0ed-f102bc2f3204:1,值为1,设置{anyLock}:37f75873-494a-439c-a0ed-f102bc2f3204:1:rwlock_timeout:1,

值为1,并且将上面俩个俩个值的有效期设置为30000毫秒,就返回nil啦,加锁成功。

【 读锁&读读&读写 】

那么读读会互斥吗?从上面的源码中我们可以看出来,并不会,如果已经有一个客户端已经加锁了,那么此时的mode还是read,会进入到判断分支,hincrby自增新的锁key [ID:threadId],这里这么做,有俩个好处,如果是同一个客户端同一个线程,就会自增1,如果不是的话,就会新增一个kv,[ID2:threadId2],也会和之前一样拼接一个rwlock_timeout的kv,,并且给所有的key设置有效期为30000毫秒。到这里,很清晰了,读锁的读读是不会互斥的。

那么读写会互斥吗?假设我们已经加了一把读锁,此时有人过来加写锁呢?这样,我们先来分析分析写锁的加锁机制,再来回答这个问题。【结论:读读不互斥,读写互斥

【 写锁&加锁 】

写锁的逻辑是怎样的呢?

首先还是先获取了当前的mode是什么,我们先假设第一次加写锁,此时还是直接进入分支,设置模式为write,设置[ID:threadId1:write]为1,最后再将锁key的有效期设置为30000毫秒;好啦,这里我们来看看,读写是否互斥,此时假设已经有人加了读锁,此时来加写锁,直接就是闭门羹,返回了ttl的剩余有效期,那么客户端就会进入循环去尝试获取锁,【结论:读写是互斥的

【 写锁&写读 】

当一个客户端已经加了写锁之后,这个时候新的客户端来加读锁,这里就要看情况了,因为看代码中会有所区分,如果是当前持有写锁的客户端的当前线程来加读锁,是没问题的,如果不是,就直接返回剩余时间,一直循环等待加锁了,【结论:写读看情况,如果是持有写锁的客户端线程加读锁,ok,否则互斥

【 写锁&写写 】

如果是写写呢?此时已经有用户加了写锁,这时,一个新客户端来尝试加写锁,此时也会进行判断,如果是当前持有写锁的客户端线程,那么就会进入分支,也是操作一些自增,延长时间的操作,如果不是呢,就会返回剩余时间,进入等待循环尝试加锁了。【结论:写写互斥,但是可重入

【 读锁&watchdog机制&可重入 

redisson一般加锁的watchdog机制我们都已经分析过了,请看【汪~汪~汪~redisson的WatchDog是如何看家护院的? 】,而读锁的watchdog有一些特殊的逻辑,这里我们也来看看,首先显示获取了这个锁key对应的hash结构中是否存在加锁,如果存在,就会先将锁key的有效期延长为30000毫秒,接着hlen查看hash结构中数量,如果大于1就进入分支,获取所有key,并且进行遍历,获取到值为number的key,其实就是为了拼接得出{anyLock}:37f75873-494a-439c-a0ed-f102bc2f3204:1:rwlock_timeout:1

所有这样的key,并且将这些可以的时间都延长,这里我们也可以看出来,读锁是可重入的,然后就返回继续定时续期锁key的有效期。

读锁 &释放

接下来我们继续看看读锁的释放逻辑,这个脚本略长,我们来细细分析一波:释放逻辑呢,主要分成几种情况,我们一一来看。

首先,如果mode值不存在那么也就直接返回1了,先判断一波当前客户端线程是否存在加读锁,如果不存在,说明有问题,返回nil;接下来也说明了读锁存在,先对读锁的值-1,返回了当前的counter值,如果等于0,就直接把锁key删除hdel,紧接着我们就去删除rwlock_timeout对应的(counter+1)key删除;接下来还是去判断hash机构中的key的数量,

情况1:如果大于1,去遍历所有的key,遍历所有的rwlock_timeout,并且pttl获取他们的剩余时间,并且拿到了这些key值中最大的有效期时间maxRemainTime,如果大于0,就将锁key的有效期设置为maxRemainTime,并且返回0 ,这时候释放锁不成功。如果是写模式呢,直接返回0;

情况2:如果小于1,就直接删除锁key,释放锁成功。

对于读锁的释放,因为存在可重入性,所以逻辑上要进行循环判断,只有全部都释放了,读锁才会真正释放了。

【 写锁&释放锁 】

写锁的释放,写锁也是存在可重入的,当model是write的时候,

先判断当前线程的写锁是否存在,如果不存在,返回nil;否则,也是先对写锁值-1,如果值大于0,就返回0,释放锁不成功;否则,就开始进行删除操作,将写锁对应的值都删除,还有个小细节,这里回去判断hlen key的个数,如果等于1,说明没有客户端持有锁,删除锁key,否则说明有读锁持有,就要将mode模式转为read;返回1,释放锁成功。

招式五 信号量

信号量,Semaphore,这种锁机制很特殊,在很多特殊场景也会得到运用,他可以指定允许获取锁的线程个数,允许指定数量的多个客户端线程获取同一把锁,一旦其中某个线程释放了,其他线程也可以获得这把锁。他的实现原理是怎么样的呢?

我们在使用api加锁的时候会传入permits,允许加锁的客户端线程个数,这里加锁的逻辑并不复杂,第一次加锁,判断当前key对应的数量【value】和【permits=1】的大小,如果大于直接进入分支,对当前锁key的值-1,返回1,加锁成功,可以想象,一旦经过多次-1,value=0的时候,0>=1不成立,就会返回0,这个客户端线程就需要循环等待尝试加锁了。

释放锁的逻辑更简单,每次释放锁,就对锁key的value进行+1,这样子,新的客户端加锁就能获取成功。so easy~~

【招式六 CountDownLatch 闭锁】

这也是一把好玩的锁,使用此锁,需要指定数量的客户端线程都加锁,否则其他加锁的线程会阻塞住,直到所有的客户端都加锁成功才会进行往下的逻辑,什么样的场景适用呢,如果我们某个业务操作需要多个步骤都完成才能进行下一个步骤,那么就可以用这个锁啦,直接锁住,等待所有任务完成,释放锁之后,就可以进行下一步操作了

那么他是怎么实现的呢?

首先肯定是设置数量trySetCount(count),很简单,就是对key设置了

数量n,紧接着就需要n个线程去操作countdown(),这其中的逻辑也很简单,就是每次操作就会将key的值-1,直到key的值为0的时候,就会删除掉这个key,

而其他地方会有一个操作,就是await(),他会不断循环获取key的值,直到值等于0才会往下操作,是不是很简单,就实现了这一机制。

【 招式七 可重入非公平锁 】

可重入非公平锁的机制我们在前面的篇文章中已经讲解过了,参见【扒开Redisson的小棉袄,Debug深入剖析分布式锁之可重入锁No.1】,回顾一下吧。

对于redisson分布式锁框架的解构到这里就差不多了,是不是觉得简单一招一式,也成就了分布式锁凌厉的剑气,对于上下俩篇的解构,其中涉及了七种分布式锁的机制,若文中有描述错误的,欢迎小伙伴指正批评,悉心接受。谢谢大家观看。

万剑归宗之七剑下天山,redisson的百锁解构(下)相关推荐

  1. 万剑归宗,redisson的百锁解构(上)

    万剑归宗,剑至高境,剑谱上虽是普通招式,可是练至大成,以指为剑,剑气破苍穹.redisson框架也是如此,通过lua脚本实现了可重入非公平锁,公平锁,非公平锁,联合锁,信号量,读写锁,countDow ...

  2. windows下mysql主从同步_详解windows下mysql的主从同步

    半路出家到Java,刚开始听说到说程序支持读写分离感觉特别高大上,也一直没接触 偶然的机会接触到了,就一定得记下来. 今天先讲讲数据库的主从同步,两个好处: 是读写分离可以用上.比如 写操作就写到主数 ...

  3. linux下grep命令用法,全面详解linux下grep命令的用法

    学linux系统,grep这个命令是必须要掌握的.grep命令是用来查找文件或标准输出中匹配的行,它的强大之处在于它支持正则表达式.日常工作中,grep绝对是用的最多的命令之一. 注:本文只介绍gre ...

  4. 聊聊 ES6 解构(下)

    大家好,我是Chuck,一个不那么正经却热爱前端的孩儿. 昨晚睡觉的时候,突然想起来点事情,点开了掘金,一看上文,这是什么东西?怎么忘了好多东西呢? 这篇文章接上文 聊聊 ES6 解构|8月更文挑战. ...

  5. Redisson实现分布式锁(3)—项目落地实现

    Redisson实现分布式锁(3)-项目落地实现 有关Redisson实现分布式锁前面写了两篇博客作为该项目落地的铺垫. 1.Redisson实现分布式锁(1)-原理 2.Redisson实现分布式锁 ...

  6. 基于后端开发Redisson实现分布式锁源码分析解读

    一.分布式锁的概念和使用场景 分布式锁是控制分布式系统之间同步访问共享资源的一种方式. 在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问 ...

  7. 告毕业生书——七剑送你下天山

    七剑送你下天山 --告大学毕业生书 杨义先 教授 北京邮电大学信息安全中心主任 灾备技术国家工程实验室主任 公共大数据国家重点实验室主任 即将毕业的同学们: 武侠练剑的最高境界是:手中无剑,剑在心中! ...

  8. 现身说法:实际业务出发分析百亿数据量下的多表查询优化

    今天给大家带来的讨论主题是通过实战经验来对百亿数据量下的多表数据查询进行优化,俗话说的好,一切脱离业务的架构都是耍流氓,接下来我就整理一下今天早上微信群里石头哥给大家分享的百亿数据量多表查询架构以及优 ...

  9. #python计算结果百位500向下取整,(0-499取000,500-999取500)

    !/usr/bin/env python coding:utf-8 计算结果百位500向下取整,(0-499取000,500-999取500) import math calc_Amount = fl ...

  10. 剑指offer——面试题58:二叉树的下一个结点

    剑指offer--面试题58:二叉树的下一个结点 这个题要认真分析一下... Solution1: 自己想出来的破算法 /* struct TreeLinkNode {int val;struct T ...

最新文章

  1. UC伯克利教授Stuart Russell人工智能基础概念与34个误区
  2. vim复制内容到系统剪贴板
  3. Linux驱动 - 多线程之 完成量
  4. Visual Studio无法查找或打开 PDB 文件解决办法
  5. 开源上网行为管理_做好企业上网行为管理作用大
  6. 个人使用mysql_MySql使用总结
  7. C++ vector是什么?应该如何理解
  8. python爬虫步骤-Python爬虫怎么入门-初级篇
  9. 零基础如何入门MATLAB(适用于所有编程语言)?(建议收藏)
  10. 1H413000工业机电工程安装技术—— 1H413010机械设备安装技术
  11. 零点和极点到底影响了什么?什么是最小相位系统?
  12. 大概的知识点 有点乱(乱的一批)
  13. 计算机接口接触不良,如何处理电脑耳机插口接触不良
  14. uniapp小程序实现开屏页
  15. 好书推荐-——《态度》——吴军老师著
  16. hazelcast java_Hazelcast
  17. RequestMapping的映射URL模板
  18. 话机耳机模式进入保持状态下每隔1分钟会切换到免提模式后再切回耳机模式
  19. Android Wear控制PPT播放软件的开发
  20. ctfshow---vip限免题目11~20

热门文章

  1. CentOS 6忘记密码解决方法(三分钟超快o)
  2. 自然语言三兄弟NLP、NLU、NLG傻傻分不清?一文搞懂它们的区别
  3. 网页显示不正常怎么修复
  4. Annotated Potholes Image Dataset下载
  5. CA数字证书是什么意思?SSL证书与CA数字证书有什么区别?
  6. codelite开发php,C++跨平台开发环境(CodeLite)
  7. [转载]MIT人工智能实验室:如何做研究
  8. c语言情话编程,用c语言写的情话
  9. N95滤材之父:滤材才是关键!道翰天琼认知智能机器人平台API接口大脑为您揭秘-2。
  10. Grafana的介绍与使用