读写锁的使用场景及锁降级
发布日期:2021-11-18 19:17:20 浏览次数:8 分类:技术文章

本文共 3080 字,大约阅读时间需要 10 分钟。

读写锁的使用场景及锁降级

互斥锁

为了更好的理解读写锁的使用,首先我们需要了解一下什么是互斥锁?

对于多线程来说存在线程的不同状态,有新生状态,就绪状态,运行状态,阻塞状态,死亡状态。线程也可以通过相应命令进行相应的调度。那么为了保证数据的一致性,也就是操作的原子性,此时就需要进行锁的处理。
而所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程),原子操作是不可分割的,在执行完毕之前不会被任何其它任务或事件中断。意思就是这种操作是单位级的操作,执行过程绝对不受影响,执行结果一定。
互斥锁就是通过一种简单的加锁的方法来控制对共享资源的访问。互斥锁只有两种状态,即上锁( lock )和解锁( unlock )。

读写锁的应用场景

我们对数据的操作无非两种:“读”和“写”,试想一个这样的情景,当十个线程同时读取某个数据时,这个操作应不应该加同步。答案是没必要的。只有以下两种情况需要加同步:

1.这十个线程对这个公共数据既有读又有写
2.这十个线程对公共数据进行写操作
也就是说有对数据进行改变的操作就需要同步上锁。
假设有这么一个场景,代码示例如下:

public Object getData(String key){
rw.readLock().lock();//在读前先上读锁 Object result = null; try{
result = map.get(key); //这个if比较关键,它避免了多余的对数据库的读取 if(result==null){
//如果内存中没有所要数据 rw.readLock().unlock(); rw.writeLock().lock(); if(result==null){
try{
//我们用这个代替对数据库访问得到数据的步骤 result = "new"; }finally{
rw.writeLock().unlock(); } rw.readLock().lock(); } } }finally{
rw.readLock().unlock(); } return result; }

关于unlock操作,我们知道只要是上了锁就必须要解锁,但是有这么一种情况就是当你上完锁后在执行解锁操作前程序出现异常,那这个所可能就一直存在。所以针对这个问题我们一般将unlock操作放在finally代码块中,就可以保证上了的锁一定会被解。
上面的两次if判断,第一个if相信大家很好理解。但为什么要用第二个if呢?再假设一个场景,现在有十个线程来读这个数据,而这个数据又不存在与缓存区,那么这十个线程中最先到的线程将执行“rw.writeLock().lock();”而另外九个线程将被阻塞,当第一个线程读完以后缓存区实际上已经就有了这个数据,但另外九个阻塞在“rw.writeLock().lock();”如果不加这层if他们会继续访问数据库,由此可见加了这层if对整个过程影响很大。

锁降级

锁降级指的是写锁降级成为读锁。如果当前线程拥有写锁,然后将其释放,最后再获取读锁,这种分段完成的过程不能称之为锁降级。锁降级是指把持住(当前拥有的)写锁,再获取到读锁,随后释放(先前拥有的)写锁的过程。

修改之前的代码示例如下:

public Object getData(String key){
rw.readLock().lock();//在读前先上读锁 Object result = null; try{
result = map.get(key); //这个if比较关键,它避免了多余的对数据库的读取 if(result==null){
//如果内存中没有所要数据 rw.readLock().unlock(); rw.writeLock().lock(); if(result==null){
try{
//我们用这个代替对数据库访问得到数据的步骤 result = "new"; }finally{
//当前线程拥有写锁,再获取到读锁,再释放写锁 rw.readLock().lock(); rw.writeLock().unlock(); } } } }finally{
rw.readLock().unlock(); } return result; }

锁降级的必要性:

锁降级中读锁的获取是否必要呢?答案是必要的。主要是为了保证数据的可见性,如果当前线程不获取读锁而是直接释放写锁, 假设此刻另一个线程(记作线程T)获取了写锁并修改了数据,那么当前线程无法感知线程T的数据更新。如果当前线程获取读锁,即遵循锁降级的步骤,则线程T将会被阻塞,直到当前线程使用数据并释放读锁之后,线程T才能获取写锁进行数据更新。

这时因为可能存在一个事务线程不希望自己的操作被别的线程中断,而这个事务操作可能分成多部分操作更新不同的数据(或表)甚至非常耗时。如果长时间用写锁独占,显然对于某些高响应的应用是不允许的,所以在完成部分写操作后,退而使用读锁降级,来允许响应其他进程的读操作。只有当全部事务完成后才真正释放锁。
所以总结下锁降级的意义应该就是:在一边读一边写的情况下提高性能。

转载地址:https://blog.csdn.net/weixin_39174824/article/details/102537336 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:设计模式详解(1)——单例模式
下一篇:go mod使用笔记

发表评论

最新留言

关注你微信了!
[***.104.42.241]2024年03月18日 16时08分44秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

java gas station_LeetCode – 774. Minimize Max Distance to Gas Station 2019-04-21
java项目无法加载到tomcat_eclipse+tomcat添加项目进来无法启动tomcat 2019-04-21
后缀树建立 java_实用算法实现-第 8 篇后缀树和后缀数组 [2 最长公共子串] 2019-04-21
java网络编程期末试题_java网络编程面试题【其中一小部分】 2019-04-21
estore java_estore2 - WEB源码|JSP源码/Java|源代码 - 源码中国 2019-04-21
java如何做表单校验_微信小程序实现表单校验功能 2019-04-21
matlab dwt2(),MATLAB小波变换指令及其功能介绍(超级有用) 2019-04-21
php sequelize,egg.js整合数据库ORM框架Sequelize 2019-04-21
php同时打开2个数据库,thinkphp3.2同时连接两个数据库的简单方法 2019-04-21
centos 开发php扩展,centos 安装php扩展redis 2019-04-21
php+跑buth,php 中函数获取可变参数的方法, 这个语法有点像 golang 语言中的 2019-04-21
cms 单点登录 php,Yii2 中实现单点登录的方法 2019-04-21
oracle自己运行,创建Oracle自动执行Job 2019-04-21
oracle报错00020,oracle启动 ORA-00020: maximum number of processes (%s) exceeded错误 2019-04-21
chmod 赋权所有_chmod 权限 命令详细用法 2019-04-21
html代码翻译_[译]您知道 HTML 的键盘标签吗? 2019-04-21
html抽奖代码_JavaScript高手之路:封装抽奖效果 2019-04-21
hadoop 3.3 一直停留在running wordcount_蛋价持续下跌,今日跌破3.3元大关!深秋季节价格还能反弹吗?... 2019-04-21
的流程图做完后如何保存_2019超火的半永久眉是哪款?做完后我们如何护理?... 2019-04-21
去除logo 高德地图api_深圳品牌logo升级如何保持原型的同时更具创新? 2019-04-21