Java中常见的锁策略

目录

 乐观锁 vs 悲观锁

悲观锁:

乐观锁:

重量级锁 vs 轻量级锁  

⾃旋锁(Spin Lock)

公平锁 vs 非公平锁  

可重⼊锁 vs 不可重入锁  

读写锁 

 


乐观锁 vs 悲观锁

悲观锁:
总是假设最坏的情况,每次去拿数据的时候都认为别⼈会修改,所以每次在拿数据的时候都会上锁,这样别⼈想拿这个数据就会阻塞直到它拿到锁。
乐观锁:
假设数据⼀般情况下不会产⽣并发冲突,所以在数据进⾏提交更新的时候,才会正式对数据是否产⽣并发冲突进⾏检测,如果发现并发冲突了,则让返回⽤⼾错误的信息,让⽤⼾决定如何去做。

举个栗⼦: 同学 A 和 同学 B 想请教⽼师⼀个问题.
同学 A 认为 "⽼师是⽐较忙的, 我来问问题, ⽼师不⼀定有空解答". 因此同学 A 会先给⽼师发消息: "⽼师你忙嘛? 我下午两点能来找你问个问题嘛?" (相当于加锁操作) 得到肯定的答复之后, 才会真的来问问题. 如果得到了否定的答复, 那就等⼀段时间, 下次再来和⽼师确定时间. 这个是悲观锁.

 同学 B 认为 "⽼师是⽐较闲的, 我来问问题, ⽼师⼤概率是有空解答的". 因此同学 B 直接就来找⽼师.(没加锁, 直接访问资源) 如果⽼师确实⽐较闲, 那么直接问题就解决了. 如果⽼师这会确实很忙, 那么同学 B也不会打扰⽼师, 就下次再来(虽然没加锁, 但是能识别出数据访问冲突). 这个是乐观锁.

这两种思路不能说谁优谁劣, ⽽是看当前的场景是否合适.
如果当前⽼师确实⽐较忙, 那么使⽤悲观锁的策略更合适, 使⽤乐观锁会导致 "⽩跑很多趟", 耗费额外的资源.
如果当前⽼师确实⽐较闲, 那么使⽤乐观锁的策略更合适, 使⽤悲观锁会让效率⽐较低.

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

就好⽐同学 C 开始认为 "⽼师⽐较闲的", 问问题都会直接去找⽼师.
但是直接来找两次⽼师之后, 发现⽼师都挺忙的, 于是下次再来问问题, 就先发个消息问问⽼师忙不忙, 再决定是否来问问题

重量级锁 vs 轻量级锁  

锁的核⼼特性 "原⼦性", 这样的机制追根溯源是 CPU 这样的硬件设备提供的.
  • CPU 提供了 "原⼦操作指令".
  • 操作系统基于 CPU 的原⼦指令, 实现了 mutex 互斥锁.
  • JVM 基于操作系统提供的互斥锁, 实现了 synchronized ReentrantLock 等关键字和类.

 注意, synchronized 并不仅仅是对 mutex 进⾏封装, 在 synchronized 内部还做了很多其

他的⼯作
重量级锁: 加锁机制重度依赖了 OS 提供了 mutex
  • ⼤量的内核态⽤⼾态切换
  • 很容易引发线程的调度

这两个操作, 成本⽐较⾼. ⼀旦涉及到⽤⼾态和内核态的切换, 就意味着 "沧海桑⽥" 

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

  • 少量的内核态⽤⼾态切换.
  • 不太容易引发线程调度.
理解⽤⼾态 vs 内核态
想象去银⾏办业务.
在窗⼝外, ⾃⼰做, 这是⽤⼾态. ⽤⼾态的时间成本是⽐较可控的.
在窗⼝内, ⼯作⼈员做, 这是内核态. 内核态的时间成本是不太可控的.
如果办业务的时候反复和⼯作⼈员沟通, 还需要重新排队, 这时效率是很低的.

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

自旋锁(Spin Lock)

按之前的⽅式,线程在抢锁失败后进⼊阻塞状态,放弃 CPU,需要过很久才能再次被调度.
但实际上, ⼤部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。没必要就放弃 CPU. 这个时候就可以使⽤⾃旋锁来处理这样的问题.
如果获取锁失败, ⽴即再尝试获取锁, ⽆限循环, 直到获取到锁为⽌. 第⼀次获取锁失败, 第⼆次的尝试会在极短的时间内到来. ⼀旦锁被其他线程释放, 就能第⼀时间获取到锁
理解⾃旋锁 vs 挂起等待锁
想象⼀下, 去追求⼀个⼥神. 当男⽣向⼥神表⽩后, ⼥神说: 你是个好⼈, 但是我有男朋友了~~
挂起等待锁: 陷⼊沉沦不能⾃拔.... 过了很久很久之后, 突然⼥神发来消息, "咱俩要不试试?" (注意, 这个很⻓的时间间隔⾥, ⼥神可能已经换了好⼏个男票了).
⾃旋锁: 死⽪赖脸坚韧不拔. 仍然每天持续的和⼥神说早安晚安. ⼀旦⼥神和上⼀任分⼿, 那么就能⽴刻抓住机会上位.
⾃旋锁是⼀种典型的 轻量级锁 的实现⽅式.
  • 优点: 没有放弃 CPU, 不涉及线程阻塞和调度, ⼀旦锁被释放, 就能第⼀时间获取到锁.
  • 缺点: 如果锁被其他线程持有的时间⽐较久, 那么就会持续的消耗 CPU 资源. (⽽挂起等待的时候是不消耗 CPU 的)

synchronized 中的轻量级锁策略⼤概率就是通过⾃旋锁的⽅式实现的.  

公平锁 vs 非公平锁  

假设三个线程 A, B, C. A 先尝试获取锁, 获取成功. 然后 B 再尝试获取锁, 获取失败, 阻塞等待; 然后 C 也尝试获取锁, C 也获取失败, 也阻塞等待.
当线程 A 释放锁的时候, 会发⽣啥呢?
公平锁: 遵守 "先来后到". B ⽐ C 先来的. 当 A 释放锁的之后, B 就能先于 C 获取到锁.
⾮公平锁: 不遵守 "先来后到". B 和 C 都有可能获取到锁
这就好⽐⼀群男⽣追同⼀个⼥神. 当⼥神和前任分⼿之后, 先来追⼥神的男⽣上位, 这就是公平锁; 如果
是⼥神不按先后顺序挑⼀个⾃⼰看的顺眼的, 就是⾮公平锁.
注意:
  • 操作系统内部的线程调度就可以视为是随机的. 如果不做任何额外的限制, 锁就是⾮公平锁. 如果要想实现公平锁, 就需要依赖额外的数据结构, 来记录线程们的先后顺序.
  • 公平锁和⾮公平锁没有好坏之分, 关键还是看适⽤场景.

 synchronized 是⾮公平锁.

可重入锁 vs 不可重入锁  

可重⼊锁的字⾯意思是“可以重新进⼊的锁”,即允许同⼀个线程多次获取同⼀把锁。
⽐如⼀个递归函数⾥有加锁操作,递归过程中这个锁会阻塞⾃⼰吗?如果不会,那么这个锁就是可重⼊锁(因为这个原因可重⼊锁也叫做递归锁)。
Java⾥只要以Reentrant开头命名的锁都是可重⼊锁,⽽且JDK提供的所有现成的Lock实现类,包括 synchronized关键字锁都是可重⼊的
⽽ Linux 系统提供的 mutex 是不可重⼊锁.
理解 "把⾃⼰锁死"
⼀个线程没有释放锁, 然后⼜尝试再次加锁.
// 第⼀次加锁, 加锁成功lock();// 第⼆次加锁, 锁已经被占⽤, 阻塞等待. lock();
按照之前对于锁的设定, 第⼆次加锁的时候, 就会阻塞等待. 直到第⼀次的锁被释放, 才能获取到第⼆个锁. 但是释放第⼀个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想⼲了, 也就⽆法进⾏解锁操作. 这时候就会 死锁.

 

 这样的锁称为 不可重⼊锁.

 synchronized 是可重入锁

读写锁 

多线程之间,数据的读取⽅之间不会产⽣线程安全问题,但数据的写⼊⽅互相之间以及和读者之间都 需要进⾏互斥。如果两种场景下都⽤同⼀个锁,就会产⽣极⼤的性能损耗。所以读写锁因此而产⽣。
读写锁(readers-writer lock),看英⽂可以顾名思义,在执⾏加锁操作时需要额外表明读写意图,复数读者之间并不互斥,⽽写者则要求与任何⼈互斥。
⼀个线程对于数据的访问, 主要存在两种操作: 读数据 和 写数据.
  • 两个线程都只是读⼀个数据, 此时并没有线程安全问题. 直接并发的读取即可.
  • 两个线程都要写⼀个数据, 有线程安全问题.
  • ⼀个线程读另外⼀个线程写, 也有线程安全问题.
读写锁就是把读操作和写操作区分对待. Java 标准库提供了 ReentrantReadWriteLock 类, 实现
了读写锁
  • ReentrantReadWriteLock.ReadLock 类表⽰⼀个读锁. 这个对象提供了 lock / unlock ⽅法 进⾏加锁解锁.
  • ReentrantReadWriteLock.WriteLock 类表⽰⼀个写锁. 这个对象也提供了 lock / unlock ⽅法进⾏加锁解锁
其中,
  • 读加锁和读加锁之间, 不互斥.
  • 写加锁和写加锁之间, 互斥.
  • 读加锁和写加锁之间, 互斥.
注意, 只要是涉及到 "互斥", 就会产⽣线程的挂起等待. ⼀旦线程挂起, 再次被唤醒就不知道隔了多久了.
因此尽可能减少 "互斥" 的机会, 就是提⾼效率的重要途径

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

 Synchronized 不是读写锁.

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

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

相关文章

AES加密解密算法

一,AES算法概述 AES属于分组加密,算法明文长度固定为128位(单位是比特bit,1bit就是1位,128位等于16字节) 而密钥长度可以是128、192、256位 当密钥为128位时,需要循环10轮完成加密&#xff0…

【Threejs基础教程-光影篇】5.2 Threejs 阴影系统

5.2 Threejs阴影系统 学习ThreeJS的捷径在用光影系统之前threejs是实时光影web端目前没有优质的实时光影实时光影会大幅增加渲染压力没有独显的电脑不建议添加实时光影 阴影配置什么样的灯光可以产生阴影什么样的物体可以产生阴影和接受阴影注意开启阴影渲染灵活运用阴影 平行光…

判断一个数据能否同时被3和5整除

一、运行结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>int main() {//初始化变量值&#xff1b;int a 0;//提示用户printf("请输入一个整数\n");//获取用户输入数据&#xff1b;scanf("%d", &am…

Spring Boot项目启动过程中为什么日志打印没有显示完整包名呢?

一、前言 不知道大家注意过没有&#xff0c;在Spring Boot项目启动过程中日志打印并没有显示完整的报名&#xff0c;而是显示一些o.a.c&#xff0c;o.s.web形式的包名&#xff0c;如下图&#xff1a; 这是为什么呢&#xff1f; 二、原理 首先&#xff0c;我们先看一下Spring…

WordPress AutomaticPlugin SSRF漏洞复现(CVE-2024-27954)

0x01 产品简介 WordPress是一款免费开源的内容管理系统(CMS),最初是一个博客平台,但后来发展成为一个功能强大的网站建设工具,适用于各种类型的网站,包括个人博客、企业网站、电子商务网站等,并逐步演化成一款内容管理系统软件。 0x02 漏洞概述 WordPress AutomaticPlu…

01-XML-04XML处理

XML处理 DOM DOM解析要求解析器将整个XML文件全部加载到内存中&#xff0c;生成一个Document对象。 优点&#xff1a;元素和元素之间保留结构&#xff0c;关系&#xff0c;可以针对元素进行增删改查操作。 缺点&#xff1a;如果XML文件过大&#xff0c;可能会导致内存溢出。SA…

【QT入门】 QListWidget各种常见用法详解之列表模式

往期回顾 【QT入门】 Qt代码创建布局之setLayout使用-CSDN博客 【QT入门】 Qt代码创建布局之多重布局变换与布局删除技巧-CSDN博客 【QT入门】 QTabWidget各种常见用法详解-CSDN博客 【QT入门】 QListWidget各种常见用法详解之列表模式 QListWidget有列表和图标两种显示模式&a…

springboot论坛管理系统

论坛管理系统 摘要&#xff1a; 在社会快速发展的影响下&#xff0c;论坛管理系统继续发展&#xff0c;使论坛管理系统的管理和运营比过去十年更加信息化。依照这一现实为基础&#xff0c;设计一个快捷而又方便的网上论坛管理系统是一项十分重要并且有价值的事情。对于传统的论…

一篇讲明白 Hadoop 生态的三大部件

文章目录 每日一句正能量前言01 HDFS02 Yarn03 Hive04 HBase05 Spark及Spark Streaming关于作者推荐理由后记赠书活动 每日一句正能量 黎明时怀着飞扬的心醒来&#xff0c;致谢爱的又一天&#xff0c;正午时沉醉于爱的狂喜中休憩&#xff0c;黄昏时带着感恩归家&#xff0c;然后…

【Linux】进程实践项目 —— 自主shell编写

送给大家一句话&#xff1a; 不管前方的路有多苦&#xff0c;只要走的方向正确&#xff0c;不管多么崎岖不平&#xff0c;都比站在原地更接近幸福。 —— 宫崎骏《千与千寻》 自主shell命令编写 1 前言2 项目实现2.1 创建命令行2.2 获取命令2.3 分割命令2.4 运行命令 3 源代码…

非NVIDIA平台下的CUDA的替代方案OpenCL,第一步如何获取PlatformInfo、DeviceInfo

非NVIDIA平台下的CUDA的替代方案OpenCL&#xff0c;第一步如何获取PlatformInfo、DeviceInfo 介绍 当谈到高性能计算&#xff0c;NVIDIA的CUDA框架无疑是一个强大的工具。OpenC&#xff08;Open Computing Language&#xff09;是一个更为通用的解决方案&#xff0c;或者你使用…

Vscode连接远程服务器中的docker容器进行开发

0.预安装 1.本地windows或其他环境中安装了Vscode&#xff0c;Vscode中安装了Remote-SSH拓展&#xff08;用于利用SSH连接docker容器&#xff09; 2.远程服务器中安装了docker&#xff0c;并且拉取了自己需要的镜像 3.有root权限&#xff0c;能使用sudo命令 1. 在服务器端启…

javaWeb项目-学生考勤管理系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、JAVA技术 JavaSc…

蓝桥杯省赛刷题——题目 2656:刷题统计

刷题统计OJ链接&#xff1a;蓝桥杯2022年第十三届省赛真题-刷题统计 - C语言网 (dotcpp.com) 题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天做 a 道题目&#xff0c;周六和周日每天做 b 道题目。请你帮小明计算&#xff0c;按照计划他将在第几…

MS Edge浏览器坏了?网页播放视频的速度不对

前言 小白是MS Edge浏览器的重度用户。电脑上必须有的两个浏览器&#xff1a;Google Chrome和Microsoft Edge。 前段时间小白在使用MS Edge的时候出了问题&#xff1a;播放视频或者音频的时候总是被莫名其妙加速或者减速&#xff0c;类似于播放视频时候的0.5x或者2.0x。 当时…

C++入门知识详细讲解

C入门知识详细讲解 1. C简介1.1 什么是C1.2 C的发展史1.3. C的重要性1.3.1 语言的使用广泛度1.3.2 在工作领域 2. C基本语法知识2.1. C关键字(C98)2.2. 命名空间2.2 命名空间使用2.2 命名空间使用 2.3. C输入&输出2.4. 缺省参数2.4.1 缺省参数概念2.4.2 缺省参数分类 2.5. …

Abaqus周期性边界代表体单元Random Sphere RVE 3D (Mesh)插件

插件介绍 Random Sphere RVE 3D (Mesh) - AbyssFish 插件可在Abaqus生成三维具备周期性边界条件(Periodic Boundary Conditions, PBC)的随机球体骨料及骨料-水泥界面过渡区(Interfacial Transition Zone, ITZ)模型。即采用周期性代表性体积单元法(Periodic Representative Vol…

python distribute是什么

Python的包管理工具常见的有easy_install, setuptools, 还有pip, distribute&#xff0c;那麽这几个工具有什么关系呢&#xff0c;看一下下面这个图就明白了&#xff1a; 可以看到distribute是setuptools的替代方案&#xff0c;pip是easy_install的替代方案。 Distribute提供一…

【QT学习】2.补充:connect中的lambda表达式

一.简单实例&#xff1a; 1.实例要求 点击按钮&#xff0c;实现 >o<与#-#的转换。 2.步骤 补充&#xff1a;​​​​​​​ 1.如果我想在lambda中修改数据&#xff0c;怎么办&#xff1f; 写上mutable就行。

vue基础教程(5)——构建项目级登录页

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、创建首页二、登录页代码讲解三、对应的vue知识点&#xff1a;四、附件-各文件代码总结 前言 前面我们已经把vue自带的页面删除&#xff0c;也搭建了最简单的router路由&#xff0c;下面就可以真正开发我们自己的项目…