Reentrantreadwritelock应用和原理

news2024/12/30 2:06:11

目录

一、介绍

二、应用

三、原理


一、介绍

读操作远远高于写操作时,这时候使用读写锁让读-读可以并发,提高性能

类似于数据库中的共享锁 select...from...lock in share mode  

提供一个数据容器类内部分别使用读锁保护数据的read()方法,写锁保护数据的write()方法

两个线程读读不是互斥的,读写和写写是互斥的

注意事项:

  • 读锁不支持条件变量
  • 重入时升级不支持:持有读锁的情况获取写锁,会导致获取写锁永久等待,必须要先释放读锁才能获取写锁
  • 重入时降级支持:持有写锁可以获取读锁

二、应用

我们更新缓存有两种操作

先删除缓存:我们在实现缓存和数据库的时候就要考虑这种问题,比如现在有a线程去查询数据库然后准备把数据库的数据更新到缓存的时候,b线程把数据库的时候就改了,这个时候如果更新成功缓存就和数据库已经改了的数据不一致的,然后后面就一直拿缓存中的旧数据。

先更新数据库:先更新数据库的话好一点,缓存数据库不一致的情况会少一些,但是要保证强一致性,还是要加锁。因为先改库还没来得及删除缓存的时候,这个时候去查询都是旧数据,不一致的

这里加锁是可以用读写锁来优化的

  • 读锁:如果缓存中有数据,查询缓存,这里要记得释放读锁(因为如果缓存没有要查询需要写锁,持有读锁拿不了写锁)
  • 写锁:缓存中没有数据,从数据库中查询,写入缓存(如果很多个线程去抢锁可能会进队列很多个,但是这个只需要一个,所以要加双重检查)
  • 写锁:更新,先更新数据库然后删除缓存

这种优化主要体现在了读多写少的场景中读读可以共享的情况,但是如果还想更加提高并发,可以更细粒度划分。而且这种情况只适合单机场景

三、原理

读写锁用的是同一个sycn同步器因此等待队列、state等也是同一个

但是也有区别,他的state变了,因为要记录读锁和写锁两个锁的状态,高16位记录读锁,低16位记录写锁。其他流程都跟reentrantlock差不多

加写锁

加写锁和之前没有什么区别,先去检查state状态,如果状态等于0说明读和写锁都没有加,就再判断一个方法(非公平直接返回false,公平锁就去检查队列是否有,有就得排队进去,没有也返回),然后cas来加锁,成功就设置当前持锁线程到exclusiveOwnerThread。如果前面判断state不为0,那么可能读锁加了还是写锁加了,如果加了读锁,就互斥了直接return false,如果加的是写锁,看是不是自己加的,自己加的就是重入写锁状态+1,如果是别人的也return false,当然如果重入次数超过一定的次数65535也会抛出异常

加读锁

首先判断状态,然后看写锁的部分是不是0,如果不是0&&加写锁的是不是他自己,如果不是自己加的写锁,就返回-1(写锁是可以升级加读锁的,但是读锁不能加写锁)。如果为-1他就会再判断一遍能不能拿到读锁,还是不行就循环进入堵塞队列堵塞等待。

注意:

他们堵塞的线程虽然都在一个堵塞队列里,但是他们因为申请的锁不同他们的状态是不同的,等待读锁的是shared状态,等待写锁的是ex状态,然后前面的节点状态都是-1有责任区唤醒后面的节点,但是最后一个节点的状态是0,后面没他需要唤醒的

写锁释放

直接把状态数量-1,减一后查看是不是0,如果不是0就是锁重入减一了,返回false;如果减小后是0了,就返回true。返回真的话就把当前持锁线程设为null,然后就去检查堵塞队列的头节点,如果有气切状态不等于0,就唤醒他。唤醒了就继续循环去竞争锁,发现没人拿锁,就cas加写锁,高位加锁,然后return 1表示加锁成功了。然后就要取堵塞队列里面唤醒,如果是读锁,就唤醒。

换完阻塞队列的节点之后,拿到当前节点的下一个,如果下个节点是想要加读锁的shared,就把会当前节点的-1变为0,然后对后面的节点唤醒,然后让读锁的状态+1(多个线程都读锁可以让计数增加)然后把这个节点在堵塞队列删掉改为下一个,然后继续判断下一个是不是读节点,如果是重复。

读锁释放

拿到state状态,然后把读锁状态-1,然后用cas去设置state,看看能不能成功,成功后判断是不是0,如果是true,不是0就是false。如果是0就去看堵塞队列的头结点,如果头结点的状态是-1就唤醒那个节点,不是-1就重试。

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

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

相关文章

Android 9-SystemUI:(1)启动流程

具体分析(以下代码示例,讲解,都是通过,Android9代码来举例) SystemUI,其实是可以看作是一个系统级的服务,也就是SystemUIService, SystemUI的服务启动,要从SystemServer.run()方法入手 main 方法里启动了…

JavaWeb小记—响应对象response

目录 响应对象response的原理图 response——响应对象 响应文本数据 响应字节数据 验证码案例 响应状态码 1.http状态返回代码 1xx(临时响应) 2.http状态返回代码 2xx (成功) 3.http状态返回代码 3xx (重定向…

2023.6.12-6.18 AI行业周刊(第151期):AI创业项目交付部署,困难和机遇并存

这段时间,工作上项目上的事情,开始进入了一个快车道,很多项目开始并行。所以每天白天的时候,被各种事情填充的很满。 加入华勤后从0到1组建的团队,其实本身也是创业属性,从市场->售前->算法->视频…

Godot 4 源码分析 - 练手 - 和谐共生

今天看到一个微信视频,和谐共生,大概效果如下 https://live.csdn.net/v/306826 研究这么长时间的Godot,今天试试能否实现上述效果 粗看一下,这个效果实现分几步: 1. 画圆,并确定多个圆的位置规律 2. 动…

UE4/5动画系列(2.怎么套模板)

目录 大象套模板 动画同步(这个在模板里面开同步): 速度限制: 穿墙问题: 在之前我们已经做了一个基础的模板了: UE4/5动画蓝图模板制作和套模板(1.模板制作)_多方通行8的博客-C…

【计算机组成原理】总线

目录 一、总线概述 二、总线的分类 三、系统总线的结构 四、总线的性能指标 五、总线仲裁 六、总线操作和定时 七、总线标准 一、总线概述 总线:是一组能为多个部件分时共享信息的传送线路 早期计算机外部设备少时大多采用分散连接方式,不易实现…

【C语言初阶】带你轻松玩转所有常用操作符(1)

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,这里是君兮_,最近要准备期末复习了,可能更新的就不会那么频繁了,下个星期回复正常更新。 操作符详解1 前言一.操作符的分类二.算数操作符三.移位操作符1.二进制表示的三种形式2.…

【C/C++】构造函数与析构函数

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

【话题研究】重塑活力:顺应消费需求变化,PC市场需创新、技术驱动和营销策略更优解

话题研究&#xff1a;大众还需要PC吗&#xff1f;PC市场如何走出寒冬&#xff1f; 1️⃣ PC市场进入寒冬的深层原因2️⃣ PC仍具有独特的优势和不可替代性3️⃣ 创新、定制化和用户体验4️⃣ AI、VR时代带来的新出路 市场调研机构 Canalys数据显示&#xff0c;今年一季度&#…

elastic-job-ui在使用druid作为数据库连接池时作业维度报错

问题说明&#xff1a; 我们项目中使用到了elastic-job&#xff0c;然后自己封装了个sdk&#xff0c;方便使用&#xff0c;里面的数据源配置是常用的druidmysql的组合&#xff0c;在操作中&#xff0c;发现elastic-job-ui可视化控制台会报错无法使用。 深究其原因是因为&#…

返回值封装,异常统一处理优雅解决接口所有问题

在项目整体架构设计的时候&#xff0c;我们经常需要做以下工作&#xff1a; 返回值的统一封装处理&#xff0c;因为只有规范好统一的返回值格式&#xff0c;才能不会给接口使用者带来疑惑和方便前端对接口的统一处理。对异常码进行严格规定&#xff0c;什么错误返回什么码制&a…

ShardingSphere-JDBC 5.1.1 分库分表

分库分表解决的问题 mysql的扩展 mysql并不能完全利用高性能服务器的硬件&#xff0c;当cpu超过24个&#xff0c;内存超过128G时&#xff0c;mysql性能处于平缓&#xff0c;不在上升&#xff0c;所以在一个性能强大的服务器上运行多个实例&#xff0c;才更合理 mysql常见的扩…

java.sql.SQLException: No value specified for parameter 6

异常 java.sql.SQLException: No value specified for parameter 6 原因 sql中定义了6个参数&#xff0c;只传了5个参数

设计模式—“领域规则”

在特定领域中,某些变化虽然频繁,但可以抽象为某种规则。这时候,结合特定领域,将问题抽象为语法规则,从而给出在该领域下的一般性解决方案。 典型模式有:Interpreter Interpreter 动机 在软件构建过程中,如果某一个特定领域的问题比较复杂,类似的结构不断重复出现,…

.NET Microsoft.Extensions.Logging + NLog 记录日志到文件

最近想了解下面向对象开发&#xff0c;选择C# 语言 以及NET6.0 日志是开发中最常用的功能&#xff0c;本文记录下其中日志使用方法&#xff0c;理解不全的地方后续再学习补充 环境 Ubuntu 22.04.2 LTSdotnet 6.0.411 准备工作 # https://learn.microsoft.com/zh-cn/dotnet/c…

2023.6.21AgentGPT部署

在云服务器上使用Docker部署AgentGPT 需要自行提供OpenAI的API Key https://platform.openai.com/account/api-keys 需要自行提供云服务器或者虚拟机 需要自行解决网络的问题&#xff0c;本文中使用的是小喵咪解决网络的问题【需要订阅地址】 文章目录 在云服务器上使用Docker…

数据在内存中的存储-浮点型

常见的浮点型数据&#xff1a;单精度浮点型float、双精度浮点型double,还有long double类型。 浮点数表示的范围&#xff1a;float.h中定义 目录 一、浮点数存储的例子 二、浮点数存储规则 三、例题解释 一、浮点数存储的例子 #include<stdio.h> int main() {int …

王道操作系统学习笔记(1)——操作系统基本概念

前言 本文介绍了操作系统的基本概念&#xff0c;文章中的内容来自B站王道考研操作系统课程&#xff0c;想要完整学习的可以到B站官方看完整版。 一&#xff1a;操作系统基本概念 1.1.1&#xff1a;基本概念和功能 操作系统&#xff1a;系统资源的管理者&#xff08;处理机管…

QGIS 插件获取哨兵数据

基于 Sentinel Hub QGIS 插件&#xff0c;该插件允许您直接在 QGIS中配置和利用Sentinel Hub 服务的强大功能。该插件可视化 Sentinel 数据&#xff0c;可用于正在处理的任何其他项目中。 来自&#xff1a;GIS数据栈整理&#xff1a;GIS数据栈 一起来看看如何在QGIS中使用吧&am…

6张图表 + 1个案例 带你入门tcpdump的使用和原理

一、tcpdump简介 tcpdump是什么&#xff1f; 来看看 tcpdump官网怎么说&#xff1a;This is the home web site of tcpdump, a powerful command-line packet analyzer; and libpcap, a portable C/C library for network traffic capture. 不妨来看看chatGPT插件怎么说&…