当前位置: 首页 > news >正文

常见的锁策略

注意: 接下来讲解的锁策略不仅仅是局限于 Java . 任何和 "锁" 相关的话题, 都可能会涉及到以下内容. 这些特性主要是给锁的实现者来参考的.普通的程序猿也需要了解一些, 对于合理的使用锁也是有很大帮助的.
 

1.乐观锁 vs 悲观锁

悲观锁:

(认为出现锁冲突是大概率事件)

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会

上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

乐观锁:

(认为出现锁冲突的小概率事件)

假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是

否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误的信息,让用户决定如何

去做。

Synchronized 初始使用乐观锁策略. 当发现锁竞争比较频繁的时候, 就会自动切换成悲观锁策略.

乐观锁适用场景

乐观锁的一个重要功能就是要检测出数据是否发生访问冲突. 我们可以引入一个 "版本号" 来解决.

假设我们需要多线程修改 "用户账户余额".

设当前余额为 100. 引入一个版本号 version, 初始值为 1. 并且我们规定 "提交版本必须大于记录
当前版本才能执行更新余额

线程 A 此时准备将其读出( version=1, balance=100 ),线程 B 也读入此信息( version=1,
balance=100 ).



 

 线程 A 操作的过程中并从其帐户余额中扣除 50( 100-50 ),线程 B 从其帐户余额中扣除 20
( 100-20 );

 

线程 A 完成修改工作,将数据版本号加1( version=2 ),连同帐户扣除后余额( balance=50
),写回到内存中;

线程 B 完成了操作,也将版本号加1( version=2 )试图向内存中提交数据( balance=80
),但此时比对版本发现,操作员 B 提交的数据版本号为 2 ,数据库记录的当前版本也为 2 ,不
满足 “提交版本必须大于记录当前版本才能执行更新“ 的乐观锁策略。就认为这次操作失败.


 

 2.读写锁

synchronized不是读写锁!

多线程之间,数据的读取方之间不会产生线程安全问题,但数据的写入方互相之间以及和读者之间都需要进行互斥。如果两种场景下都用同一个锁,就会产生极大的性能损耗。所以读写锁因此而产生。
读写锁(readers-writer lock),看英文可以顾名思义,在执行加锁操作时需要额外表明读写意图,复数读者之间并不互斥,而写者则要求与任何人互斥。

一个线程对于数据的访问, 主要存在两种操作: 读数据 和 写数据

  • 两个线程都只是读一个数据, 此时并没有线程安全问题. 直接并发的读取即可.
  • 两个线程都要写一个数据, 有线程安全问题.
  • 一个线程读另外一个线程写, 也有线程安全问题.
     

读写锁就是把读操作和写操作区分对待. Java 标准库提供了 ReentrantReadWriteLock 类, 实现了读写锁.

  • ReentrantReadWriteLock.ReadLock 类表示一个读锁. 这个对象提供了 lock / unlock 方法进行加锁解锁.
  • ReentrantReadWriteLock.WriteLock 类表示一个写锁. 这个对象也提供了 lock / unlock 方法进行加锁解锁.

 其中,

  • 读加锁和读加锁之间, 不互斥.
  • 写加锁和写加锁之间, 互斥.
  • 读加锁和写加锁之间, 互斥.
     

注意, 只要是涉及到 "互斥", 就会产生线程的挂起等待. 一旦线程挂起, 再次被唤醒就不知道隔了多久了.因此尽可能减少 "互斥" 的机会, 就是提高效率的重要途径.

适用场景:

读写锁特别适合于 "频繁读, 不频繁写" 的场景中. (这样的场景其实也是非常广泛存在的)

比如教务系统.
每节课老师都要使用教务系统点名, 点名就需要查看班级的同学列表(读操作). 这个操作可能要每周执行好几次.而什么时候修改同学列表呢(写操作)? 就新同学加入的时候. 可能一个月都不必改一次.再比如, 同学们使用教务系统查看作业(读操作), 一个班级的同学很多, 读操作一天就要进行几十次上百次.但是这一节课的作业, 老师只是布置了一次(写操作)

 

3.重量级锁 vs 轻量级锁

synchronized 开始是一个轻量级锁. 如果锁冲突比较严重, 就会变成重量级锁.

锁的核心特性 "原子性", 这样的机制追根溯源是 CPU 这样的硬件设备提供的

  • CPU 提供了 "原子操作指令".
  • 操作系统基于 CPU 的原子指令, 实现了 mutex 互斥锁.
  • JVM 基于操作系统提供的互斥锁, 实现了 synchronized 和 ReentrantLock 等关键字和类.

 

 注意, synchronized 并不仅仅是对 mutex 进行封装, 在 synchronized 内部还做了很多其他的
工作

重量级锁: 加锁机制重度依赖了 OS 提供了 mutex

  • 大量的内核态用户态切换
  • 很容易引发线程的调度

这两个操作, 成本比较高. 一旦涉及到用户态和内核态的切换, 就意味着 "沧海桑田".

轻量级锁: 加锁机制尽可能不使用 mutex, 而是尽量在用户态代码完成. 实在搞不定了, 再使用 mutex.

  • 少量的内核态用户态切换.
  • 不太容易引发线程调度

理解用户态 vs 内核态
想象去银行办业务.
在窗口外, 自己做, 这是用户态. 用户态的时间成本是比较可控的.
在窗口内, 工作人员做, 这是内核态. 内核态的时间成本是不太可控的.
如果办业务的时候反复和工作人员沟通, 还需要重新排队, 这时效率是很低的.

4.自旋锁(Spin Lock)

synchronized开始是自旋锁,后面锁竞争激烈就成了挂起等待锁

synchronized 中的轻量级锁策略大概率就是通过自旋锁的方式实现的.

按之前的方式,线程在抢锁失败后进入阻塞状态,放弃 CPU,需要过很久才能再次被调度.
但实际上, 大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。没必要就放弃 CPU. 这个时候就可以使用自旋锁来处理这样的问题.

自旋锁伪代码:

while (抢锁(lock) == 失败) {}

如果获取锁失败, 立即再尝试获取锁, 无限循环, 直到获取到锁为止. 第一次获取锁失败, 第二次的尝试会在极短的时间内到来.一旦锁被其他线程释放, 就能第一时间获取到锁.

理解自旋锁 vs 挂起等待锁

想象一下, 去追求一个女神. 当男生向女神表白后, 女神说: 你是个好人, 但是我有男朋友了~~
挂起等待锁: 陷入沉沦不能自拔.... 过了很久很久之后, 突然女神发来消息, "咱俩要不试试?" (注意,这个很长的时间间隔里, 女神可能已经换了好几个男票了).
自旋锁: 死皮赖脸坚韧不拔. 仍然每天持续的和女神说早安晚安. 一旦女神和上一任分手, 那么就能立刻抓住机会上位.

自旋锁是一种典型的 轻量级锁 的实现方式

自旋锁也是一种典型的 乐观锁 的实现方式

挂起等待锁也可以认为是重量级锁的典型实现方式

挂起等待锁也可以认为是悲观锁的典型实现方式

  • 优点: 没有放弃 CPU, 不涉及线程阻塞和调度, 一旦锁被释放, 就能第一时间获取到锁.
  • 缺点: 如果锁被其他线程持有的时间比较久, 那么就会持续的消耗 CPU 资源. (而挂起等待的时候是不消耗 CPU 的

5.公平锁 vs 非公平锁

synchronized内部实现是非公平锁

公平锁:

如果多个线程发生了锁竞争,当锁被释放的时候,这些等待的线程,遵循先来后到的顺序来拿到锁。

非公平锁:

如果多个线程发生了锁竞争,当锁被释放的时候,这些线程不遵循先来后到(这些线程谁都有机会拿到锁,机会均等)

  • 操作系统内部的线程调度就可以视为是随机的. 如果不做任何额外的限制, 锁就是非公平锁. 如果要想实现公平锁, 就需要依赖额外的数据结构(队列), 来记录线程们的先后顺序.
  • 公平锁和非公平锁没有好坏之分, 关键还是看适用场景.
     

6.可重入锁 vs 不可重入锁

synchronized 是可重入锁

可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁。
比如一个递归函数里有加锁操作,递归过程中这个锁会阻塞自己吗?如果不会,那么这个锁就是可重入锁(因为这个原因可重入锁也叫做递归锁)。
Java里只要以Reentrant开头命名的锁都是可重入锁,而且JDK提供的所有现成的Lock实现类,包括synchronized关键字锁都是可重入的。
而 Linux 系统提供的 mutex 是不可重入锁
 

相关文章:

常见的锁策略

注意: 接下来讲解的锁策略不仅仅是局限于 Java . 任何和 "锁" 相关的话题, 都可能会涉及到以下内容. 这些特性主要是给锁的实现者来参考的.普通的程序猿也需要了解一些, 对于合理的使用锁也是有很大帮助的. 1.乐观锁 vs 悲观锁 悲观锁: (认为出现锁冲…...

springboot学习(八十) springboot中使用Log4j2记录分布式链路日志

在分布式环境中一般统一收集日志,但是在并发大时不好定位问题,大量的日志导致无法找出日志的链路关系。 可以为每一个请求分配一个traceId,记录日志时,记录此traceId,从网关开始,依次将traceId记录到请求头…...

10种ADC软件滤波方法及程序

10种ADC软件滤波方法及程序一、10种ADC软件滤波方法1、限幅滤波法(又称程序判断滤波法)2、中位值滤波法3、算术平均滤波法4、递推平均滤波法(又称滑动平均滤波法)5、中位值平均滤波法(又称防脉冲干扰平均滤波法&#x…...

第五章:Windows server加域

加入AD域:教学视频:https://www.bilibili.com/video/BV1xM4y1D7oL/?spm_id_from333.999.0.0首先我们选择一个干净的,也就是新建的没动过的Windows server虚拟机。我们将DNS改成域的ip地址,还要保证它们之间能ping的通&#xff0c…...

Elasticsearch:获取 nested 类型数组中的所有元素

在我之前的文章 “Elasticsearch: object 及 nested 数据类型” 对 nested 数据类型做了一个比较详细的介绍。在实际使用中,你在构建查询时肯定会遇到一些问题。根据官方文档介绍,nested 类型字段在隐藏数组中索引其每个项目,这允许独立于索引…...

English Learning - Day53 作业打卡 2023.2.7 周二

English Learning - Day53 作业打卡 2023.2.7 周二引言1. 我必须承认,我之前学习没你用功。have to VS must2. 这跟我想得一样简单。3. 生活并不像它看上去那么顺风顺水,但也不会像我们想象得那么难。Look VS seem4. 你比去年高多了。5. 你关心你的工作胜…...

SpringMVC--注解配置SpringMVC、SpringMVC执行流程

注解配置SpringMVC 使用配置类和注解代替web.xml和SpringMVC配置文件的功能 创建初始化类,代替web.xml 在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类, 如果找到的话就用它来配置Servle…...

JavaScript中数组常用的方法

文章目录前言常用数组方法1、 join( )2、push()与 pop()3、shift()与 unshift()4、sort()5、reverse()6、slice(&#xff…...

ModuleNotFoundError: No module named ‘pip‘

项目场景:pip 错误 Traceback (most recent call last): File "E:\KaiFa\Python\Python38\lib\runpy.py", line 194, in _run_module_as_main return _run_code(code, main_globals, None, File "E:\KaiFa\Python\Python38\lib\runpy.py&qu…...

ROS2 入门应用 发布和订阅(C++)

ROS2 入门应用 发布和订阅(C)1. 创建功能包2. 创建源文件2.1. 话题发布2.2. 话题订阅3. 添加依赖关系4. 添加编译信息4.1. 添加搜索库4.2. 增加可执行文件4.3. 增加可执行文件位置5. 编译和运行1. 创建功能包 在《ROS2 入门应用 工作空间》中已创建和加…...

XSS漏洞,通过XSS实现网页挂马

**今天讲下通过XSS实现网页挂马~*,目的是了解安全方面知识,提升生活网络中辨别度 原理: 实验分为两部分: 1、通过Kali linux,利用MS14_064漏洞,制作一个木马服务器。存在该漏洞的用户一旦通过浏览器访问木…...

家政服务小程序实战教程09-图文卡片

小程序还有一类需求就是展示服务的列表,我们这里用图文卡片组件来实现,我们先要添加一个标题,使用网格布局来实现 第一列添加一个文本组件,第二列添加一个图标组件 修改文本组件的文本内容,设置外边距 设置第二列的样式…...

国内唯一一部在CentOS下正确编译安装和使用RediSearch的教程

开篇 Redis6开始增加了诸多激动人心的模块,特别是:RedisJSON和RediSearch。这两个模块已经完全成熟了。它们可以直接使用我们的生产上的Redis服务器来做全文搜索(二级搜索)以取得更廉价的硬件成本、同时在效率上竟然超过了Elastic…...

前端对于深拷贝和浅拷贝的应用和思考

浅拷贝 浅拷贝 : 浅拷贝是指对基本类型的值拷贝,以及对对象类型的地址拷贝。它是将数据中所有的数据引用下来,依旧指向同一个存放地址,拷贝之后的数据修改之后,也会影响到原数据的中的对象数据。最简单直接的浅拷贝就…...

Java基础常见面试题(三)

String 字符型常量和字符串常量的区别? 形式上: 字符常量是单引号引起的一个字符,字符串常量是双引号引起的若干个字符; 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算;字符串常量代表一个地址值…...

C++设计模式(13)——装饰模式

亦称: 装饰者模式、装饰器模式、Wrapper、Decorator 意图 装饰模式是一种结构型设计模式, 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 问题 假设你正在开发一个提供通知功能的库, 其他程序可使用它向用户发…...

ESP-01S通过AT指令上报数据到阿里云物模型

ESP-01S使用AT指令上报数据到阿里云物模型 上篇文章介绍了如何用AT指令连接阿里云并进行通信:https://blog.csdn.net/weixin_46251230/article/details/128995530 但最终需要将传感器数据上报到云平台显示,所以需要建立阿里云物模型 阿里云平台建立物…...

【强化学习】马尔可夫决策过程MDP

1.马尔可夫决策过程MDP 1.1 MDP五元组 MDP<S,A,P,R,γ>MDP<\mathcal{S},\mathcal{A},\mathcal{P},\mathcal{R},\mathcal{\gamma}>MDP<S,A,P,R,γ>&#xff0c;其中&#xff1a; S\mathcal{S}S&#xff1a;状态空间A\mathcal{A}A&#xff1a;动作空间P\mathc…...

刘润:五维思考,让你站得更高、看得更远

原标题&#xff1a;刘润&#xff1a;五维思考&#xff0c;让你站得更高、看得更远 前言&#xff1a;遇到问题时&#xff0c;有的人很快就能想明白&#xff0c;有的人需要很久才能想明白&#xff0c;还有的人始终都想不明白。 而且&#xff0c;那些很快就能想明白的人&#xff0…...

从运维角度看微服务 k8s部署微服务【偏理论】【AL】

从运维角度看微服务 & 部署微服务【偏理论】 1、微服务的特点 服务组件化&#xff1a; 每个服务独立开发、部署&#xff0c;有效避免一个服务的修改引起整个系统重新部署。 技术栈灵活&#xff1a; 约定通信方式&#xff0c;使得服务本身功能实现对技术要求不再那么敏感。…...

专题 | 防抖和节流

一 防抖&#xff1a;单位时间内&#xff0c;频繁触发事件&#xff0c;只执行最后一次 场景&#xff1a;搜索框搜索输入&#xff08;利用定时器&#xff0c;每次触发先清掉以前的定时器&#xff0c;从新开始&#xff09; 节流&#xff1a;单位时间内&#xff0c;频繁触发事件&…...

C++入门:重载运算符和重载函数

C 允许在同一作用域中的某个函数和运算符指定多个定义&#xff0c;分别称为函数重载和运算符重载。重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明&#xff0c;但是它们的参数列表和定义&#xff08;实现&#xff09;不相同。当您调用一个重载函…...

conda 新建虚拟环境 等等

1&#xff1a;conda create -n env_name package_name #创建名为env_name的新环境&#xff0c;并在该环境下安装名为package_name 的包&#xff0c;例如&#xff1a;conda create -n Arg python3.8 # 创建名字为Arg python为3.8版本的虚拟环境2&#xff1a; conda activate env…...

【C++:STL之栈和队列 | 模拟实现 | 优先级队列 】

目录 1. stack的介绍和使用 1.1 stack的介绍 1.2 stack的使用 2 栈的模拟实现 3 queue的介绍和使用 3.1 queue的介绍 3.2 queue的使用 4 queue的模拟实现 5 deque的介绍 5.1deque的原理介绍 5.2 deque的缺陷 5.3 为什么选择deque作为stack和queue的底层默认容器 6 p…...

基于SpringBoot+Vue的疫苗预约管理系统(Java项目)

【辰兮要努力】&#xff1a;hello你好我是辰兮&#xff0c;很高兴你能来阅读&#xff0c;昵称是希望自己能不断精进&#xff0c;向着优秀程序员前行&#xff01; 博客来源于项目以及编程中遇到的问题总结&#xff0c;偶尔会有读书分享&#xff0c;我会陆续更新Java前端、后台、…...

华为OD机试 - 计算网络信号(Python),真题含思路

计算网络信号 题目 网络信号经过传递会逐层衰减,且遇到阻隔物无法直接穿透,在此情况下需要计算某个位置的网络信号值。 注意:网络信号可以绕过阻隔物 array[m][n] 的二维数组代表网格地图,array[i][j] = 0 代表 i 行 j 列是空旷位置;array[i][j] = x ( x 为正整数)代表 i …...

【Spring】注解实现IOC操作,你理解了吗?

作者&#xff1a;狮子也疯狂 专栏&#xff1a;《spring开发》 坚持做好每一步&#xff0c;幸运之神自然会驾凌在你的身上 专栏推荐&#xff1a;写文章刚刚起步&#xff0c;各个专栏的知识点后续会补充完善&#xff0c;不断更新好文&#xff0c;希望大 家支持一下。 专栏名字El…...

微搭低代码从入门到精通01-总体介绍

在过去我们开发小程序&#xff0c;要学习各类知识。比如前端知识、后端知识、服务器知识及各种中间件及数据库的知识。 要想学会这些知识&#xff0c;既需要投入大量的学习时间&#xff0c;而且要经过相当的实践才可以掌握。 如果立志从事开发行业&#xff0c;投入精力去学习…...

类的继承

类的继承&#xff1a;一个类继承另一个类&#xff0c;自动拥有这个类的属性和方法&#xff0c;类似于包含与被包含的关系。被继承的类称为父类--子类则是继承父类的类。一个父类可以有多个子类&#xff1b;一个子类可以有多个父类&#xff08;多继承&#xff09;问题创建子类时…...

应用场景一:西门子PLC通过桥接器连接MQTT服务器

应用场景描述&#xff1a; 云平台、MES等数据采集、设备管理系统&#xff0c;需要通过MQTT的方式&#xff0c;上传和下发数据&#xff0c;MQTT服务器可以获取PLC的实时状态数据&#xff0c;也可以下发控制指令。桥接器提供4G、WIFI和有线三种连接方式。 网络拓扑&#xff1a;…...

网站 dns 解析/今日国际军事新闻头条

1半导体的重要性IT行业快速发展&#xff0c;而处理器无疑起到引领行业趋势的重要作用。长期以来&#xff0c;处理器的生产制造对于外界来说非常的神秘。在处理器方面&#xff0c;我国发展较晚&#xff0c;在顶尖技术方面相比国外的处理器厂商差距很大。处理器的生产流程由晶圆制…...

做淘宝内部优惠券网站要钱么/百度关键词工具入口

Hello, I’m Shendi 最近小程序要接入微信的内容安全方面 这里记录一下 获取 Token 封装 首先这两个接口都需要使用到 Token 这里我直接贴上代码&#xff0c;其中的参数都是直接追加在 url 上 JSON方面使用的是 fastjson /** * 判断返回的数据是否为错误数据.* param json…...

企业网站内容如何搭建/东莞seo网络公司

用Python对刑侦科推理题进行分析发布时间&#xff1a;2020-06-24 10:02:37来源&#xff1a;亿速云阅读&#xff1a;105作者&#xff1a;清晨这篇文章主要介绍用Python对刑侦科推理题进行分析&#xff0c;文中示例代码介绍的非常详细&#xff0c;具有一定的参考价值&#xff0c;…...

公司网站的开发和网版的重要性/seo推广怎么学

不传之术&#xff1a;80%胜率的波浪理论技法&#xff0c;交易期权的视频↓↓期权公式 诺贝尔经济学奖资料来源&#xff1a;雪球1。合约方向如上一篇文章所述&#xff0c;期权合约有两种类型&#xff1a;看涨期权和看跌期权。也称为看涨期权和看跌期权。期权公式 诺贝尔经济学奖…...

wordpress部署阿里云/网络培训机构

物联网学习入门篇之MQTT协议 MQTT: Massage Queuing Telemetry Transport,消息队列遥测传输 MQTT是基于互联网的基础协议TCP/IP协议而构建的&#xff0c;由IBM在1999年发布&#xff0c;基于发布和订阅两种模式&#xff08;publish/subscribe&#xff09;算是一种轻量级通讯协议…...

网站构建培训/seo关键词库

前段时间&#xff0c;朋友让我推荐一款运动耳机&#xff0c;说是给孩子跑步的时候使用。对了&#xff0c;他还提出了耳机最好能脱离手机听歌的要求。其实这也不是什么难事啊&#xff0c;作为一个运动爱好者&#xff0c;我也是比较喜欢一边听歌一边跑步&#xff0c;其中也用不少…...