乐vs悲观锁,重vs轻量级锁,公vs非公平锁,不vs可重入锁,等等锁策略

这里讲的“乐观锁”“悲观锁”“轻量级锁”等等,都不是一个锁,而是一类锁。 

比如:我们班有“带眼镜”的同学,这里“带眼镜”并不是指一个人,而是指一类人。

并且这里的锁,并不局限于Java,而是只要关于有锁的话题,就会有下面的讨论。

目录

一、乐观锁vs悲观锁

二、轻量级锁vs重量级锁

三、公平锁vs非公平锁

四、自旋锁vs挂起等待锁

五、可重入锁vs不可重入锁

六、读写锁

七、Synchronized锁的原理

八、锁消除,锁粗化


一、乐观锁vs悲观锁

我们“ 预测这个锁冲突的概率高不高  ”这个角度

如果高,就是悲观锁;如果低,就是乐观锁。

乐观锁因为概率不高,做的工作可以轻松点;

悲观锁因为概率高,做的工作就是复杂点的。

这两种锁是站在:看加锁解锁的 “过程” 所干的活多还是少

乐观锁:就像一个乐观的人,总觉得别人都是好的,事件的发生都是往好的发展,所以它会认为别人不会经常修改这个数据。

悲观锁:就像一个悲观的人,总觉得别人是坏的,事情的发生都是往坏的发展,所以它会觉得别人老想修改这个数据。

他们应对的策略也不一样。 

 举个栗⼦: 同学 A 和 同学 B 想请教⽼师⼀个问题。

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

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

如果当前⽼师确实⽐较忙, 那么使⽤悲观锁的策略更合适, 使⽤乐观锁会导致 "⽩跑很多趟", 耗费额外的资源。

如果当前⽼师确实⽐较闲, 那么使⽤乐观锁的策略更合适, 使⽤悲观锁会让效率⽐较低。

二、轻量级锁vs重量级锁

这个站在 加锁解锁的开销大还是小 的角度

轻量级锁:加锁解锁开销小;重量级锁:加锁解锁开销大。

这两种锁是站在 “结果” 的角度去看最终加锁解锁的时间是多还是少。

为什么轻量级锁开销就小,重量级锁开销就大? 

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

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

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

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

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

而重量级锁是加锁机制重度依赖了 OS 提供了 mutex互斥锁。

  • ⼤量的内核态⽤⼾态切换
  • 很容易引发线程的调度

那啥是内核态,啥是用户态?


比如说:

1)在银行你要办业务,可能会需要身份证或者户口本复印件,这时候,你如果没带,那么银行可能会有复印机。

2)那么你有2种选择:第一种是去柜台让柜员帮你,第二种是自己去大厅的复印机复印。

3)内核态:如果让柜员帮你去后台复印,可能没这么快,柜员也许要帮别人也复印,也可能去摸鱼,也可能去上个厕所。我们知道操作系统是由内核和软件组成,有很多软件都需要内核管理,可能会没那么及时。

4)用户态:如果你是自己去大厅复印,自给自足,非常的快。

三、公平锁vs非公平锁

 大家可以思考一下:什么是公平

1)先到先得

2)虽然你先到,但是我们概率均等

在计算机中,我们的公平锁其实是遵循(1)先到先得

这就好⽐⼀群男⽣追同⼀个⼥神。

公平锁:当⼥神和前任分⼿之后, 先来追⼥神的男⽣上位

⾮公平锁:如果是⼥神不按先后顺序挑⼀个⾃⼰看的顺眼的

注意:

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

四、自旋锁vs挂起等待锁

当一个线程释放锁了之后,另外的线程就会去抢这个锁,如果没抢到那就会进入堵塞状态,放弃CPU,但是其实没过多久就再能抢锁了,没必要放弃CPU。这时候就需要自旋锁了。

自旋锁:当争抢锁失败之后不气馁,不放弃CPU,继续等待争抢锁,其实没过多久就会有下一次尝试了。一直循环,直到抢到锁。

挂起等待锁:当争抢锁失败了之后,就放弃CPU,可能会等好久才能去竞争。

⾃旋锁是⼀种典型的 轻量级锁 的实现⽅式

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

 举个例子:

两个人追女神,但是这时候女神已经有男朋友了。

自旋锁:看到女神有男朋友也不放弃,一直不断的追求女神,每天早安午安晚安问候女神,等女神分手了,就问女神处不处对象~

挂起等待锁:看到女神有男朋友了,直接暂时放弃追求女神,等听到或者知道女神分手了(这时候可能过了很久,失去最好的机会),才去问问女神处不处对象~ 

五、可重入锁vs不可重入锁

可重入锁:顾名思义,就是一个线程可以多次获取同一把锁。

不可重入锁:就是一个线程不可以多次获取同一把锁。

Java⾥只要以Reentrant开头命名的锁都是可重⼊锁,⽽且JDK提供的所有现成的Lock实现类,包括 synchronized关键字锁都是可重⼊的。⽽ Linux 系统提供的 mutex 是不可重⼊锁。

为什么会有不可重入锁?

其实可重入锁才是经过编写改进的,不可重入锁才是符合常态的。

里面的锁认为:我要拿到锁,就要外面的锁释放掉,我才能拿到,你不给我我就先堵塞

外面的锁认为:你在里面堵塞着我怎么才能先结束这个锁给你啊?

以上就是不可重入锁,也就是最基本的锁规则之一。

六、读写锁

在多线程中:

我们的读操作同时进行是不会有安全问题的,因为没有修改;

但是如果一个读一个写,那么就会有线程安全问题,因为修改了;

还有两个一起写也会有安全问题,因为都对数据进行了修改。

如果这时候都用来加锁,那就非常浪费资源,效率非常的低。因为我们更多时候都只需要读,如果两种场景下都⽤同⼀个锁,就会产⽣极⼤的性能损耗。所以读写锁因此⽽产⽣。

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

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

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

  • ReentrantReadWriteLock.ReadLock 类表⽰⼀个读锁。这个对象提供了 lock / unlock ⽅法进⾏加锁解锁
  • ReentrantReadWriteLock.WriteLock 类表⽰⼀个写锁。这个对象也提供了 lock / unlock ⽅法进⾏加锁解锁

我们规定:

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

值得注意的是:

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

七、Synchronized锁的原理

 Synchronized的加锁过程:

JVM 将 synchronized 锁分为:⽆锁、偏向锁、轻量级锁、重量级锁 状态。会根据情况,进⾏依次升级。

1) 偏向锁

第⼀个尝试加锁的线程, 优先进⼊偏向锁状态。

偏向锁不是真的 "加锁", 只是给对象头中做⼀个 "偏向锁的标记", 记录这个锁属于哪个线程。如果后续没有其他线程来竞争该锁, 那么就不⽤进⾏其他同步操作了(避免了加锁解锁的开销) 如果后续有其他线程来竞争该锁(刚才已经在锁对象中记录了当前锁属于哪个线程了, 很容易识别当前申请锁的线程是不是之前记录的线程), 那就取消原来的偏向锁状态, 进⼊⼀般的轻量级锁状态。偏向锁本质上相当于 "延迟加锁" 。能不加锁就不加锁, 尽量来避免不必要的加锁开销. 但是该做的标记还是得做的, 否则⽆法区分何时需要真正加锁。

举个栗⼦理解偏向锁:

假设男主是⼀个锁,⼥主是⼀个线程。如果只有这⼀个线程来使⽤这个锁,那么男主⼥主即使不领证结婚(避免了⾼成本操作),也可以⼀直幸福的⽣活下去。但是⼥配出现了, 也尝试竞争男主, 此时不管领证结婚这个操作成本多⾼, ⼥主也势必要把这个动作完成了, 让⼥配死⼼。

2) 轻量级锁

随着其他线程进⼊竞争, 偏向锁状态被消除, 进⼊轻量级锁状态(⾃适应的⾃旋锁)。此处的轻量级锁就是通过 CAS 来实现

  • 通过 CAS 检查并更新⼀块内存 (⽐如 null => 该线程引⽤)
  • 如果更新成功, 则认为加锁成功
  • 如果更新失败, 则认为锁被占⽤, 继续⾃旋式的等待(并不放弃 CPU)

⾃旋操作是⼀直让 CPU 空转, ⽐较浪费 CPU 资源. 因此此处的⾃旋不会⼀直持续进⾏, ⽽是达到⼀定的时间/重试次数, 就不再⾃旋了. 也就是所谓的 "⾃适应" 

 3) 重量级锁

如果竞争进⼀步激烈, ⾃旋不能快速获取到锁状态, 就会膨胀为重量级锁 此处的重量级锁就是指⽤到内核提供的 mutex .

  • 执⾏加锁操作, 先进⼊内核态
  • 在内核态判定当前锁是否已经被占⽤
  • 如果该锁没有占⽤, 则加锁成功, 并切换回⽤⼾态
  • 如果该锁被占⽤, 则加锁失败. 此时线程进⼊锁的等待队列, 挂起. 等待被操作系统唤醒
  • 经历了⼀系列的沧海桑⽥, 这个锁被其他线程释放了, 操作系统也想起了这个挂起的线程, 于是唤醒 这个线程, 尝试重新获取锁

八、锁消除,锁粗化

锁消除:编译器+JVM 判断锁是否可消除。如果可以,就直接消除。

比如,有的时候我们使用Synchronized,这个是锁,我们没有在多线程下使用,就会自动优化掉,提高效率(比如StringBuffer)

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

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

(锁的粒度: 粗和细)粒度就是指锁括号括起来的代码。

实际开发过程中,使⽤细粒度锁,是期望释放锁的时候其他线程能使⽤锁。但是实际上可能并没有其他线程来抢占这个锁。这种情况 JVM 就会⾃动把锁粗化, 避免频繁申请释放锁。

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

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

相关文章

优化数据的抓取规则:减少无效请求

在爬取房价信息的过程中,如何有效过滤无效链接、减少冗余请求,是提升数据抓取效率的关键。本文将介绍如何优化爬虫抓取贝壳等二手房平台中的房价、小区信息,并通过代理IP、多线程、User-Agent和Cookies的设置,确保数据抓取的稳定性…

(娱乐)魔改浏览器-任务栏图标右上角加提示徽章

一、目标: windows中,打开chromium,任务栏中会出现一个chromium的图标。我们的目标是给这个图标的右上角,加上"有1条新消息"的小提示图标,也叫徽章(badge)注意:本章节纯属娱乐,有需要…

手脱简单upx

大一下的事情,补个档 手动脱壳の新年快乐 查壳,有壳,UPX X32dbg打开文件,查看初始断点 点击PUSHAD跟进,CTRL*设置EIP,开始F8步过,寻找ESP寄存器第一次单个变红的地址 此时的内存窗口 开始步过…

esp32核心跑分程序

https://github.com/ochrin/coremark/tree/esp32 最近一直捣腾esp32s3 (Sense) 做微型摄像。过程中发现一款不错的跑分软件,特此记一笔。 其中针对esp32s3各类参数设定(用idf.py menuconfig),做个记录。 CPU Frequency去240MHz&#xff08…

【H2O2|全栈】关于CSS(6)CSS基础(五)

目录 CSS基础知识 前言 准备工作 网页项目规范 创建项目 布局 补充一部分属性 outline border-radius 预告和回顾 后话 CSS基础知识 前言 本系列博客将分享层叠样式表(CSS)有关的知识点。 本期博客主要分享的是网页项目规范,ou…

VC++以资源方式打开可执行文件

刚看一个资料说可以在VC中,以资源方式打开可执行文件,然后它如果包含对话框一些资源,会呈现出来,可以把其他程序界面上的控件直接拷贝到自己程序; 但是操作了一下没有成功, 先新建一个空对话框准备拷贝东…

Linux运维篇-服务器简介

目录 前言服务器分类(按服务器的机箱结构来划分)台式服务器机架式服务器刀片式服务器 外观部件内部结构前面板前面板组件前面板接口说明前面板指示灯和按钮前面板指示灯/按钮说明 后面板后面板组件后面板接口说明后面板指示灯后面板指示灯说明 主板和 iB…

uni-app生命周期(三)

文章目录 一、uni-app的生命周期二、应用生命周期三、页面的生命周期函数1.简介2.页面加载时序介绍3.页面加载常见问题4.页面加载顺序4.部分生命周期介绍 四、组件的生命周期函数 一、uni-app的生命周期 应用生命周期(整个App的生命周期) 在app.vue里面…

C++之仿函数和虚函数

仿函数(Functor)和虚函数(Virtual Function)是 C 中两个不同的概念,它们在功能和使用场景上有显著的区别。 1. 仿函数(Functor) 定义: 仿函数(也称为函数对象&#xf…

酒店布草洗涤-酒店分层管理编程实现--———未来之窗行业应用跨平台架构

一、添加楼层代码 未来之窗_人工智能_传送阵(添加楼层,客户信息,300,200) CyberWin_Dialog.layer(未来之窗传送,{type:"url",title:title,move:true,width:阵眼宽度"px",height:阵眼高度"px",id:未来之窗app_通用ID,mask:false,align:59,hidecl…

大数据Flink(一百二十一):Flink CDC基本介绍

文章目录 Flink CDC基本介绍 一、什么是CDC 二、CDC的实现机制 三、​​​​​​​​​​​​​​传统 CDC ETL 分析 四、​​​​​​​​​​​​​​基于 Flink CDC 的 ETL 分析 五、​​​​​​​​​​​​​​什么是 Flink CDC 六、​​​​​​​​​​​​​​…

CCF202006_1

问题描述 试题编号&#xff1a;202006-1试题名称&#xff1a;线性分类器时间限制&#xff1a;1.0s内存限制&#xff1a;512.0MB问题描述&#xff1a; 题解&#xff1a; #include<bits/stdc.h>using namespace std; int n, m;struct Node {int x, y;char ch; }node[1010…

51单片机按键数码管(简单设计)

51单片机按键数码管是一个简单的设计项目&#xff0c;使用四位数码管进行显示&#xff0c;矩阵按键加独立按键输入&#xff0c;将读取到据显示在数码管上。 一、参考PCB图 二、参考代码 #include <reg51.h> // LED数码管引脚定义 sbit LED1 P2 ^ 0; sbit LED2 P2 ^ 1;…

spark读取数据性能提升

1. 背景 spark默认的jdbc只会用单task读取数据&#xff0c;读取大数据量时&#xff0c;效率低。 2. 解决方案 根据分区字段&#xff0c;如日期进行划分&#xff0c;增加task数量提升效率。 /*** 返回每个task按时间段划分的过滤语句* param startDate* param endDate* param …

基于Leaflet和天地图的直箭头标绘实战-源码分析

目录 前言 一、Leaflet的特种标绘库 1、特种标绘对象的定义 2、Plot基类定义 3、直线箭头的设计与实现 二、在天地图中进行对象绘制 1、引入天地图资源 2、标绘对象的调用时序 3、实际调用过程 三、总结 前言 在博客中介绍过geoman标绘的具体实现&#xff0c;使用Leaf…

Window Server 2019+ 安装 Docker

刚刚在等待下载的时候&#xff0c;发了篇文&#xff0c;然后发现直接下载docker用不了&#xff0c;看了官网说明发现&#xff1a;Docker 不支持Window Server!!!不要直接下载官网那里的安装包。 那个链接点进去了windows的指南&#xff0c;发现也有问题&#xff0c;给的脚本地…

数据结构:(OJ141)环形列表

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;…

【数据类型】映射map

小明正在备考英语四级考试&#xff0c;但他的词典太厚了&#xff0c;他记不住哪个单词在哪里。 于是他准备开发一个可以直接找某单词在某页的应用。 但是&#xff0c;他不会做&#xff0c;整天十分烦恼。 好啦&#xff0c;进入正题&#xff0c;大家好&#xff0c;我是学霸小羊…

Kubernetes从零到精通(12-Ingress、Gateway API)

Ingress和Gateway API都是Kubernetes中用于管理外部访问集群服务的机制&#xff0c;但它们有不同的设计理念和适用场景。它们的基本原理是通过配置规则&#xff0c;将来自外部的网络流量路由到Kubernetes集群内部的服务上。 Ingress/Gateway API和Service Ingress/Gateway API…

Django后台管理复杂模型

【图书介绍】《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》_django 5企业级web应用开发实战(视频教学版)-CSDN博客 《Django 5企业级Web应用开发实战&#xff08;视频教学版&#xff09;》(王金柱)【摘要 书评 试读】- 京东图书 (jd.com) Django框架…