[Java EE] 多线程(七): 锁策略

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (90平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(93平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

1.几种不同的锁策略

常见的锁策略不仅仅是局限于Java中,任何语言都适用.

1.1 乐观锁vs悲观锁

  • 乐观锁:加锁的时候,预测锁冲突的概率比较小,所以在接下来的时间里做的事情就比较少,也就是加锁的开销就比较小.但是具体是怎么判断加锁之后锁冲突的概率的,这就和JVM的源代码有关联了.
  • 悲观锁:加锁的时候,预测锁冲突的概率比较大,所以在接下来的时间里做的事情就比较多,也就是加锁的开销就比较大.

举例说明:找老师问问题
现在有A和B两个同学:同学A采用悲观锁的策略,同学B采用乐观锁的策略
同学A就会想:"我去了老师办公室之后,老师也不一定有时间回答."所以就会给老师提前发微信,询问老师是否有时间得到肯定答复之后才会来问问题.
同学B就会想:"我去了之后老师肯定有时间回答."所以就会直接找老师帮忙,如果老师比较闲,那么问题便得已解决,如果老师比较忙,也不会打扰到老师.

什么时候使用乐观锁策略,什么时候使用悲观锁策略,还是要看具体的场景.
就如上述的例子,如果老师确实比较忙,使用悲观锁就是很好的策略,如果使用乐观锁,就是白跑一趟.
老师确实比较闲的时候,使用乐观锁就是很好的策略,如果使用悲观锁,就会额外消耗加锁的开销.

1.2 重量级锁vs轻量级锁

一般来说,轻量级锁对应的就是乐观锁,重量级锁对应的就是悲观锁,两组概念经常被混起来用.
我们现在来解释一下,重量级锁究竟比轻量级锁多做了哪些事情,就是轻量级锁值通过用户态代码来加锁,而重量级锁就是通过系统内核提供的mutex来加锁,也就是通过内核态实现加锁.
在这里插入图片描述

1.3 自旋锁vs挂起等待锁

自旋锁是轻量级锁的典型实现方式,挂起等待锁是重量级锁的典型实现方式.

  • 自旋锁: 按之前的方式,线程抢占锁失败就会进入阻塞等待状态,放弃CPU.
    但实际上,大部分情况下,虽然当前抢占锁失败,但是过了不久之后,锁就会被立刻释放掉,现在就没必要放弃CPU,这时候就可以使用自旋锁的方式来解决上述问题
void locker(){while(true){if(锁是否被占用){continue;}获取到锁;break;}
}

从上述伪代码中,我们可以看到自旋锁采用了忙等的策略,如果获取锁失败,就立即再尝试获取锁,无限循环,直到获取到锁为止.
这种锁的优点就是:没有放弃CPU,不涉及阻塞和调度,一旦锁被释放,就可以第一时间获取到锁.
缺点就是:消耗了更多的CPU资源,其他线程可能吃不到CPU资源.

  • 挂起等待锁:就是在一个线程与其他线程产生锁冲突的时候,就会挂起等待,线程不再采用忙等的方式,而是阻塞等待,直到这个锁被释放,然后系统可以唤醒线程,再去尝试重新获取锁.

优点:不会占用CPU资源,可以把CPU资源让给其他的线程.
缺点:需要系统去唤醒该线程,再去获取锁,这样就不可以第一时间获取到锁.

举例说明:
有请老朋友:钟离,达达利亚,荧
如果达达利亚和荧去表白,但是现在荧告诉他,他现在有男朋友,但是达达利亚是个死皮赖脸的人,一直每天给荧发消息问候寒暄.一旦荧和现男友分手,达达利亚就立马可以上位.
如果钟离和荧去表白,但是现在荧也告诉他,他现在有男朋友,由于钟离比较收敛,不再去天天烦荧.但是有一天他从别人口中听说荧分手了,此时钟离才有机会上位.
在这里插入图片描述

1.4 公平锁vs非公平锁

假设三个线程A,B,C.A先尝试获取锁,获取成功.然后B再尝试获取锁,获取失败,阻塞等待;然后C也尝试获取锁,C也获取失败,也阻塞等待.

  • 公平锁: 遵守先来后到的原则,B比C等待的时间长,在A释放锁之后,B就可以优先加锁.
  • 非公平锁: 不遵循先来后到的原则,B和C获取到锁的概率是相等的.就看谁的竞争能力比较强.

举例说明:
荧和他的男朋友在谈恋爱,突然有一天分手了,这时候达达利亚和钟离都有机会追到荧.但是现在达达利亚却比钟离追荧的时间长.
如果是公平锁的话:由于达达利亚追荧的时间比较长,那么达达利亚可以优先追到荧.
如果是非公平锁的话:这时就和追的时间没有关系了,这时候两个人追到荧的概率就是相等的,就各凭本事了.

注意:

  • 操作系统由于线程是随机调度的,这时候不做任何限制的话,那么锁就是非公平的.如果想要实现公平锁的话,必须借助额外的数据结构来判断等待时间.
  • 公平锁和非公平锁没有好坏之分,主要看使用场景.

1.5 不可重入锁vs可重入锁

前面提级过,不再赘述.
https://blog.csdn.net/2301_80050796/article/details/138041540?spm=1001.2014.3001.5501

1.6 读写锁

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

  • 两个线程都只是读一个数据,此时并没有线程安全问题,直接并发读取即可.读加锁和读加锁之间并不会产生锁互斥.
  • 如果两个线程有一个读,有一个写,此时有线程安全问题,写加锁和读加锁之间会有锁互斥.
  • 如果两个线程都在写一个数据,此时就有线程安全问题,写加锁和写加锁之间会产生锁互斥.

读写锁非常适合用在,频繁读,不频繁写的场景中.

2. synchronized原理

2.1 基本特点

  • 一开始是轻量级锁(乐观锁),如果锁冲突比较频繁,就升级为重量级锁(悲观锁)
  • 实现轻量级锁的时候大概率是使用自旋锁的策略.
  • 是一把不公平锁
  • 是可重入锁
  • 不是读写锁

2.2 synchronized的锁升级

JVM将synchronized锁分为无锁、偏向锁、轻量级锁、重量级锁状态.会根据情况,进⾏依次升级.
在这里插入图片描述

  1. 偏向锁(重点)
    第一个尝试加锁的线程,先不会对线程真正加锁.而是先进入偏向锁状态.
    偏向锁不是真正的加锁,而只是给对象头中做一个"偏向锁的标记",记录这个锁属于哪个线程,如果后期没有其他线程来与该线程进行锁竞争,那么就一直保持这个状态,就减少了加锁的开销.
    偏向锁就突出了一个字"懒",能不加锁就不加锁,避免了加锁带来的不必要的开销.但是这个偏向锁的标记又不得不做,否者分不清何时哪个线程需要真正加锁.
  2. 轻量级锁
    当有其他线程尝试与该线程进行锁竞争的时候,此时偏向锁就会升级为轻量级锁,
  3. 重量级锁
    当锁冲突不断加深的时候,就会升级为重量级锁.

举例说明:
此时比如荧和达达利亚是游走在朋友和男女朋友之间的关系,这时候如果没有其他人来与达达利亚竞争荧的时候,就一直可以保持这种关系(一直保持偏向锁),就避免了官宣这种开销比较大的操作,将来如果达达利亚想分手的时候,一句话直接让荧闭嘴:“你又不是我女朋友”.
如果有一天钟离也对荧有了感情,想要追荧,这时候达达利亚就可以立即和荧官宣,这种开销就比较大了(加锁操作),就意味着达达利亚要对荧负责了,将来如果想分手的时候,也非常麻烦,需要达达利亚一顿组合技才可以把荧踹掉.

2.3 synchronized锁优化

2.3.1 锁消除

编译器+JVM判断锁是否可消除.如果可以,就直接消除.
那么什么是锁消除呢?
有一些程序的代码中,虽然使用了synchronized关键字,但是是在单线程状态下的,比如下面这个例子.

StringBuffer sb = new StringBuffer();
sb.append("a");
sb.append("b");
sb.append("c");

上述的StringBuffer操作中,每一个append方法都会涉及加锁和解锁操作,但是这是在单线程状态下进行的,这时候就没有必要再进行加锁操作,系统就会把这层锁优化掉.

2.3.2 锁粗化

⼀段逻辑中如果出现多次加锁解锁,编译器+JVM会⾃动进⾏锁的粗化.

  • 锁的粒度
    主要看该加锁范围中包含代码的多少,包含的代码越多,就认为锁的粒度越粗,反之越细.
    在这里插入图片描述
    在我们实际开发中,使用细粒度的锁是为了避免在没有加锁的情况下,避免与其他的线程产生冲突,从而产生线程安全问题.
    但是在实际上可能并没有其他线程来抢占这个锁,这时候JVM就会自动把这几个锁粗化成更少的锁,从而减少加锁解锁的系统开销.

举例说明:
有请助教:滑稽老铁
滑稽⽼哥当了领导,给下属交代⼯作任务:⽅式⼀: • 打电话,交代任务1,挂电话. • 打电话,交代任务2,挂电话. • 打电话,交代任务3,挂电话.⽅式⼆: • 打电话,交代任务1,任务2,任务3,挂电话.显然,方式二是更高效的方案.

我们通过上面的解释可以看到,synchronized背后做的事情是非常多的,目的就是为了让Java程序员们即使不懂这些东西也可以写出高质量的代码.Java的大佬真的是为我们操碎了心!!
在这里插入图片描述

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

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

相关文章

基于MQTT通信开发的失物招领小程序

项目架构设计 这个项目采用前后端分离的方式,重新设计了两条链路来支撑程序的信息获取和传递 前端的小程序页面再启动页面渲染时,直接通过DBAPI从后端数据库获取信息,直接渲染在小程序中项目中给DBAPI的定位是快速从后端获取信息&#xff0…

SQL:NOT IN与NOT EXISTS不等价

在对SQL语句进行性能优化时,经常用到一个技巧是将IN改写成EXISTS,这是等价改写,并没有什么问题。问题在于,将NOT IN改写成NOT EXISTS时,结果未必一样。 目录 一、举例验证二、三值逻辑简述三、附录:用到的S…

【Flask 系统教程 5】视图进阶

类视图 在 Flask 中,除了使用函数视图外,你还可以使用类视图来处理请求。类视图提供了一种更为结构化和面向对象的方式来编写视图函数,使得代码组织更清晰,并且提供了更多的灵活性和可扩展性。 创建类视图 要创建一个类视图&am…

SVM直观理解

https://tangshusen.me/2018/10/27/SVM/ https://www.bilibili.com/video/BV16T4y1y7qj/?spm_id_from333.337.search-card.all.click&vd_source8272bd48fee17396a4a1746c256ab0ae SVM是什么? 先来看看维基百科上对SVM的定义: 支持向量机(英语:su…

使用Python实现二维码生成工具

二维码的本质是什么? 二维码本质上,就是一段字符串。 我们可以把任意的字符串,制作成一个二维码图片。 生活中使用的二维码,更多的是一个 URL 网址。 需要用到的模块 先看一下Python标准库,貌似没有实现这个功能的…

一文了解python机器学习Sklearn

1.3 安装和配置Sklearn 要使用Sklearn库,首先需要安装Python和相应的库。在本教程中,我们将使用Python 3.x版本。可以使用以下命令安装Sklearn库: pip install scikit-learn安装完成后,可以在Python代码中导入Sklearn库&#xf…

code-server容器webpack的ws无法连接解决方法

TLDR 通过指定client的wsrul去连接ws devServer.client.webSocketURL ‘wss://<Forwarded uri>/ws’ 拓扑 1、code-server: 用于编写代码、启动webpack dev-server 服务&#xff1b;[https://<domain>:8001] 2、webpack: 用于浏览dev-server服务&#xff1b;[ht…

在视频中使用时间卷积和半监督训练进行三维人体姿态估计

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;在视频中使用时间卷积和半监督训练进行三维人体姿态估计1、文献摘要2、提出方法2.1、时间扩张卷积模型2.2、半监督方法2.3、与传统…

UE4 Widget制作搜索框

效果&#xff1a; 一、控件层级结构 1.父控件层级结构 2.子控件层级结构 二、蓝图 1.先清除掉创建子项&#xff08;注意&#xff1a;这里使用的是reverse循环&#xff01;&#xff09; 2.判断是否含有关键字&#xff0c;创建子控件

【Android学习】日期和时间选择对话框

实现功能 实现日期和时间选择的对话框&#xff0c;具体效果可看下图(以日期为例) 具体代码 1 日期对话框 1.1 xml <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android&quo…

AGI要闻:斯坦福李飞飞首次创业,瞄准“空间智能”;OpenAI下周发布搜索产品挑战谷歌;新的开源 AI 眼镜来了|钛媒体AGI | 最新快讯

多方消息证实&#xff0c;OpenAI将会在北京时间5月10日&#xff08;周五&#xff09;凌晨2点公布搜索引擎新产品消息。 斯坦福大学首位红杉讲席教授 李飞飞 通用人工智能&#xff08;AGI&#xff09;领域又公布了一系列重磅消息。 5月4日凌晨&#xff0c;据路透社&#xff0c…

【深度学习】位置编码

一、引言 Self-Attention并行的计算方式未考虑输入特征间的位置关系&#xff0c;这对NLP来说是不可接受的&#xff0c;毕竟一个句子中每个单词都有着明显的顺序关系。Transformer没有RNN、LSTM那样的顺序结构&#xff0c;所以Transformer在提出Self-Attention的同时提出了Posi…

H.265 与 H.264 的主要区别

H.265 与 H.264 的主要区别 H.265 与 H.264 的主要区别各模块技术差异汇总宏块划分帧内预测模式帧间预测模式去块滤波ALF自适应环路滤波采样点自适应偏移&#xff08;Sample Adaptive Offset&#xff09;滤波并行化设计TileEntropy sliceDependent SliceWPP&#xff08;Wavefro…

双fifo流水线操作——verilog练习与设计

文章目录 一、案例分析二、fifo_ctrl模块设计2.1 波形设计&#xff1a;2.2 代码实现2.2.1 fifo_ctrl2.2.2 顶层文件top_fifo_ctrl&#xff08;rx和tx模块省略&#xff09;2.2.3 仿真文件tb_fifo_ctrl 2.3波形仿真 一、案例分析 案例要求&#xff1a;写一个 fifo 控制器&#x…

SPARC VScode EIDE GDB 使用配置

前言 搞了多年的SPARC 最近接触了VSCODE插件感觉好用。想想看不是能方便调试和编译SPARC&#xff0c;决定使用开源的SPARC仿真环境和编译器来试试。感觉的却不错&#xff0c;借此献给使用SPARC的朋友们。安装 1.找微软官方的下载VSCODE. 2.电机左边的方块形状的图标&#xff0…

【强训笔记】day8

NO.3 思路&#xff1a;相乘除以最大公约数等于最小公倍数。最小公倍数等于gcd&#xff08;a&#xff0c;a%b&#xff09;递归直到b等于0。 代码实现&#xff1a; #include <iostream> using namespace std;int gcd(int a,int b) {if(b0) return a;return gcd(b,a%b); }…

二叉树的迭代遍历 | LeetCode 144. 二叉树的前序遍历、LeetCode 94. 二叉树的中序遍历、LeetCode 145. 二叉树的后序遍历

二叉树的前序遍历&#xff08;迭代法&#xff09; 1、题目 题目链接&#xff1a;144. 二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3]示例 2&#x…

【北京仁爱堂】事出有因,原来是“肝”出现问题,才导致了痉挛性斜颈

痉挛性斜颈是肌张力障碍疾病中的一种&#xff0c;局限于颈部肌肉。由于颈部肌肉间断或持续的不自主的收缩&#xff0c;导致头颈部扭曲、歪斜、姿势异常。一般在30&#xff5e;40岁发病。由于痉挛性斜颈病因不明&#xff0c;西医方面药物及手术的临床疗效不甚理想&#xff0c;而…

PHP 反序列化

一、PHP 序列化 1、对象的序列化 <?php class people{public $nameGaming;private $NationLiyue;protected $Birthday12/22;public function say(){echo "老板你好呀&#xff0c;我是和记厅的镖师&#xff0c;叫我嘉明就行&#xff0c;要运货吗你&#xff1f;"…

Linux查看某一个程序的安装路径

前提 这一方法的前提条件是&#xff1a;必须是运行着的程序。 方法 这里以查找运行的nginx的安装目录为例。 查看nginx运行进程&#xff0c;查看当前进程的PID&#xff0c;例子中的PID就是7992。 nginps -aux|grep nginx执行ls -l /proc/进程号/exe&#xff0c;然后会打印…