JavaEE——常见的锁策略、CAS、synchronized 原理(八股)

news2024/11/28 18:53:08

文章目录

  • 一、常见的锁策略
    • 1.乐观锁 & 悲观锁
    • 2.轻量级锁 & 重量级锁
    • 3.自旋锁 & 挂起等待锁
    • 4.互斥锁 & 读写锁
    • 5. 公平锁 & 非公平锁
  • 二、CAS
    • 1、什么是 CAS
    • 2. CAS 的应用场景
    • 2.实现自旋锁
    • 3. CAS 中的 ABA 问题
  • 三、 Synchronized 原理

一、常见的锁策略

当前此处介绍的这些所策略不仅仅局限于 Java,任何与锁相关的特性都与这些相关。

1.乐观锁 & 悲观锁

这里要注意的是,此处说的锁并不是单指一把锁,而是一类锁。

  • 乐观锁: 通常假设不会发生冲突,只有在数据提交更新时才会对冲突进行检测,如有冲突就会返回信息,交给用户处理。
  • 悲观锁: 通常假设最坏的情况,冲突一直存在,认为每次获取数据是都会有人修改,所以每次获取数据时都会加锁。

总的来讲,就是两者对锁的竞争激烈程度的认知不同。

synchronized 既是一个悲观锁,又是一个乐观锁。
默认是乐观锁,但是发现所竞争比较激烈时,就会变成悲观锁。

2.轻量级锁 & 重量级锁

  • 轻量级锁: 轻量级加锁的开销较小,效率更高。(进行了少量内核态用户切换)
  • 重量级锁: 重量级加锁的开销较大,效率更低。(进行了大量内核态用户切换)

这里说明一个比较常见的情况(多数情况下):
多数情况下,乐观锁,是一个轻量级锁。
悲观锁,是一个重量级锁。

synchronized 既是一个轻量级锁,又是一个重量级锁。
默认是轻量级锁,当锁竞争比较激烈时,就会转换为重量级锁。

3.自旋锁 & 挂起等待锁

首先说明一下这两类锁的特性;
自旋锁,是一种典型的轻量级锁。
挂起等待锁,是一种典型的重量级锁。

下面我通过举例来解释两类锁的特点:
首先设想一个场景,这里我们邀请三为人选,分别是: 喜羊羊,美羊羊,沸羊羊

此时,沸羊羊 向 美羊羊 表白(尝试对美羊羊加锁),但是,美羊羊说,沸羊羊你是个好人,但是我有男朋友了(说明此时 喜羊羊 对美羊羊已经加锁)

呢么,沸羊羊想要上位,就只能等待(锁释放),于是有下面两种。

  • 针对自旋锁: 每天都去问候美羊羊,时刻关心她,一但她和喜羊羊分手,可以第一时间得知。快速尝试获取锁,并建立关系。
    很明显,自旋锁的形式,占用了大量的资源。
  • 针对挂起等待锁: 沸羊羊被婉拒后,暗下决心,表示我愿意等,只要你幸福开心,我就躲起来不打扰你,如果哪一天想起我,你就告诉我。
    这样就非常不确定,当 美羊羊 分手了,可能想起来沸羊羊,求安慰。但是,大概率根本不会想到。
    所以对于挂起等待锁,当真正被唤醒的时候就可能已经沧海桑田了。但是优点也就是相对节省资源。

synchronized 这里的轻量级锁,是基于自旋锁的形式实现。
synchronized 这里的重量级锁,是基于挂起等待锁实现的。

4.互斥锁 & 读写锁

  • 互斥锁: 就是提供 加锁 和 解锁 两个操作。当一个线程加锁了,另一个线程也尝试加锁就会阻塞等待。
  • 读写锁: 提供三种操作
    1.针对读加锁
    2.针对写加锁
    3.解锁
    上面的三种操作,针对的是多线程对同一个变量并发写操作,读操作没有线程安全问题。
    即就是,读锁与读锁之间没有互斥,写锁与写锁之间存在互斥,写锁与读锁之间存在互斥

synchronized 不是读写锁

5. 公平锁 & 非公平锁

在这里,我们将公平定义为先来后到

同样,这里可以设定一个场景,此时假设美羊羊分手了。

公平锁: 当美羊羊分手后,由等待队列中最早的舔狗上位。(阻塞队列中的元素根据顺序依次获取锁)
在这里插入图片描述

  • 非公平锁: 三个人都不等了,开始上去争抢,各凭本事。(阻塞队列中的元素竞争获取锁)
    在这里插入图片描述

synchronized 是非公平锁。

二、CAS

1、什么是 CAS

CAS:全称为Compare and swap,字面意思: 比较并交换

假设内存中的数据 V,旧的预期值 A,需要修改的新值 B。

  1. 比较 A 和 V 的值是否相同。(比较)
  2. 若比较的值相同,将 B 写入 V。(交换)
  3. 返回操作是否成功。

如图所示:
在这里插入图片描述
这里需要注意的是,CAS 的这个过程,并非是一段代码实现,而是通过 一条 CPU 指令实现。
也就是说 CAS 操作是原子性的,这样就可以在一定程度上回避线程安全问题。

2. CAS 的应用场景

  1. 实现原子类

因为是原子类,真实的 CAS 是一个原子硬件指令来完成的,实现的是 i++ 这样的操作,这里只能使用伪代码来辅助理解,如图:
在这里插入图片描述
在这里插入图片描述
如上图所示,设定两个线程分别实现自增。
在这里插入图片描述
此时假设线程1 优先抢占CPU
在这里插入图片描述
此时,线程2进入 CPU
在这里插入图片描述
最后返回线程各自的 oldvalue 即可!

总的来说,CAS 就是 CPU 提供给我们的一种特殊指令,通过这个指令,可以再一定程度上处理线程安全问题。

2.实现自旋锁

同样,自旋锁也是以伪代码的形式展现。如图所示:
在这里插入图片描述

3. CAS 中的 ABA 问题

什么是 ABA 问题

我们已知,CAS 在运行中,就是检查 value 和 oldvalue 是否一致。如果一致,就视为 value 的值中途没有被修改过,所以下一步交换没有问题。

需要注意的是这里的 “一致”,可能是没有改过,也可能是 改过,但是又还原回来了。

通俗来讲,就是,我买了个手机,这个手机可能是新机,也可能是翻新机这里我们不专业,无法区分!

ABA 这样的问题,在大部分情况下影响都不大。但是,仍然有极端情况不容忽视,问题如下:
假设到 ATM 上取钱,在取钱的时候,按下取钱键的一瞬间,机器故障卡了,此时我又不耐烦的多按了几下,此时就可能产生 bug,造成重复扣款的情况,如图:

在这里插入图片描述

解决方案

给要修改的值, 引入版本号. 在 CAS 比较数据当前值和旧值的同时, 也要比较版本号是否符合预期.

  • CAS 操作在读取旧值的同时, 也要读取版本号.
  • 真正修改的时候,
    1.如果当前版本号和读到的版本号相同, 则修改数据, 并把版本号 + 1.
    2.如果当前版本号高于读到的版本号. 就操作失败(认为数据已经被修改过了).

三、 Synchronized 原理

目前,我们知道 synchronized 的最基本用法是,两个线程针对同一个对象加锁,会产生阻塞等待。
对于 synchronized 内部,其实还有很多优化机制,存在的目的就是为了让锁更高效。

  1. 锁升级/锁膨胀
    当代码执行到 synchronized 代码块中实现加锁可能会经历下面几个过程,如图:
    在这里插入图片描述
  • 偏向锁: 进行加锁时,首先会进入到偏向锁这个状态。
    这里并不是真正的加锁,而是先占一个位置,如有需要就真加锁,无需要就放弃。
  • 轻量级锁: 当发生锁竞争的时候,就会从偏向锁升级为轻量级锁
    此时 synchronized 会通过 自旋 的方式进行加锁。
  • 重量级锁: 在 synchronized 进行自旋时,内部有个计数器,当自旋到一定时间后,就会自动升级成重量级锁。
    此时锁的类型是 挂起等待锁 ,是基于操作系统原生的 API 进行加锁了。

呢么有个问题,尽然能实现锁升级,呢么可不可以降级?
答案是不行。 在 JVM 的主流实现中,只有锁升级,没有锁降级。只要是锁对象,一旦被升级,就不能再回头了。

  1. 消除锁
    编译器的智能判定,看当前的代码是否真的需要加锁,如果不需要,但是程序员加了,就自动将锁消除。
  2. 锁粗化
    锁的粒度:synchronized 所包含的代码越多,粒度就越粗,包含的越少,粒度就越细。
    通常情况下,一般认为锁的粒度细一点较好。

对于这里的内容,本人整理的十分有限,不足的地方还希望大家多多指点。

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

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

相关文章

DataFun: ChatGPT背后的模型详解

ChatGPT背后的模型详解 Overview Transofrmer 各个构件都有一定的作用 Multi-head self attention 每个字的重要性不一样,学习QKV三个矩阵(query,key,value) 多组QKV RLHF ChatGPT训练过程 思维链 COT

C++_简单模拟实现string的基本结构

C中,string早于STL问世。使用string中的构造函数可以实现对string类型的字符串的一系列操作。 今天来模拟C中的string的基本结构。注意仅仅是简单模拟,string内部结构其实非常复杂,并且不同版本的IDEstring的内部结构也不尽相同。尽管有所不…

SpringBoot2+Vue2实战(十五)高德地图集成

1.地图官网&#xff1a; 高德开放平台 | 高德地图API 2.开发文档(web js) 正式集成&#xff1a; 1.再index.html中引入script标签 <script type"text/javascript" src"https://webapi.amap.com/maps?v2.0&key您申请的key值"></script>…

第五章 PCIe介绍 5.1-5.7

5.1 从PCIe的速度说起 为什么SSD要用PCIe接口&#xff1f;因为它快&#xff0c;比SATA快。 Lane&#xff1a;通道&#xff0c;PCIe最多可以有32个通道。 1. PCIe的工作模式 两个设备之间的PCIe连接&#xff0c;叫做一个Link。如下图&#xff0c;设备A和设备B是个双向连接&#…

【读书笔记】只管去做

《只管去做》是一本很容易读完的书&#xff0c;这本书是以故事的形式来阐述把愿景落实到每天的行动中的方法&#xff0c;对我们做人生规划很有帮助。

使用leaflet在html中加载天地图且去掉左上角的缩放图标以及右下角的logo

前言 我们这一节使用轻量化的javascript库leaflet来实现在html中加载天地图&#xff0c;实现类似高德地图、百度地图的效果。 效果图如下&#xff1a; 话不多说&#xff0c;进入主题&#xff01;&#xff01; 一、注册开发者权限 我们需要在天地图平台注册一个账号&#xff0…

【Qt】VS2013+QT5.6.3环境搭建

安装VS2013 略 安装Qt 安装文件&#xff1a;qt-opensource-windows-x86-msvc2013-5.6.3.exe&#xff08;官网已经不提供下载了。&#xff09; 安装步骤&#xff1a;安装到C盘根目录&#xff0c;其它略。 安装qt vs插件 1、下载地址&#xff1a; https://download.qt.io/a…

string常见功能模拟

学到string终于就不用像c语言一样造轮子了&#xff0c;接下来我们就模拟一下string方便我们更好理解string&#xff0c;首先我们都知道库里有个string&#xff0c;所以为了避免我们的string和库里的冲突&#xff0c;要用命名空间my_string将我们写的string包含在内。string的成…

精准医学时代:探索人工智能在DCA曲线下的临床医学应用

一、引言 在当今医学领域中&#xff0c;精准医学作为一种以个体差异为基础的医疗模式逐渐受到重视和应用[1]。精准医学基于个体基因组、环境和生活方式因素的综合分析&#xff0c;旨在实现个体化的预防、诊断和治疗方案&#xff0c;从而提供更好的临床结果[2]。与传统医学相比&…

MACD进阶版指标公式,提前一天判断MACD金叉

MACD是一种常用的技术分析指标&#xff0c;用于判断价格的趋势和动能&#xff0c;其原理是基于两条指数移动平均线的比较和对价格的平滑处理&#xff0c;MACD金叉是指MACD指标中的快线DIF从下方向上穿过慢线DEA。快线、慢线都是根据收盘价计算出来的&#xff0c;如果想提前一天…

STM32基础知识点总结

一、基础知识点 1、课程体系介绍 单片机概述arm体系结构STM32开发环境搭建 STM32-GPIO编程-点亮世界的那盏灯 STM32-USART串口应用SPI液晶屏 STM32-中断系统 STM32-时钟系统 STM32-ADC DMA 温湿度传感器-DHT11 2.如何学习单片机课程 多听理论、多理解、有问题及时提问 自己多…

论文阅读:基于深度学习的大尺度遥感图像建筑物分割研究

一、该网络中采用了上下文信息捕获模块。通过扩大感受野&#xff0c;在保留细节信息的同时&#xff0c;在中心部分进行多尺度特征的融合&#xff0c;缓解了传统算法中细节信息丢失的问题&#xff1b;通过自适应地融合局部语义特征&#xff0c;该网络在空间特征和通道特征之间建…

时间序列预测 | Matlab基于粒子群算法(PSO)优化径向基神经网络(PSO-RBF)的时间序列预测

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 时间序列预测| Matlab基于粒子群算法(PSO)优化径向基神经网络(PSO-RBF)的时间序列预测 评价指标包括:MAE、MBE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。 部分源码 %% 清空环境变量 warni…

2023年开放式蓝牙耳机选购指南!多款热门开放式蓝牙耳机品牌盘点

前言 大家好&#xff0c;作为专注耳机研究多年的发烧级爱好者&#xff0c;毫不夸张地说我为耳机花的钱比买衣服还多&#xff0c;很多人都在问我开放式耳机到底有没有必要买&#xff1f;答案毫无疑问是有必要&#xff01;开放式耳机佩戴舒适又安全的特质让它在耳机届风靡&#…

zabbix server is not running错误解决方法

1.错误&#xff1a;zabbix server is not running 打开zabbix server的时候&#xff0c;底部飘着一行黄色的警告字 2.解决方法 (1)关闭selinux (2)查看日志文件 #tail -f /var/log/zabbix/zabbix_server.log 发现内存溢出了 __zbx_mem_realloc(): out of memory 那…

vitepress使用

vitepress使用 初始化项目 pnpm init pnpm add vitepress vue 创建一个docs文件夹 在docs下新建index.js文件 # Hello VitePress在package.json中增加打包以及运行的指令 "scripts": {"docs:dev": "vitepress dev docs", // 本地运行调试&qu…

springboot高校党务系统

开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9

实力认可丨通付盾上榜《嘶吼2023网络安全产业图谱》31项细分领域

7月10日&#xff0c;嘶吼安全产业研究院联合国家网络安全产业园区&#xff08;通州园&#xff09;正式发布《嘶吼2023网络安全产业图谱》。通付盾入围本次图谱的基础技术与通用能力、网络与通信安全、安全服务、应用与产业安全、数据安全、开发与应用安全六大类别&#xff0c;3…

day32-Oracle+servlet

0目录 Oraclejdbcjspservlet 1.准备物料 1.1 创建Maven工程&#xff0c;导入依赖 方法1&#xff1a;在maven本地仓库repo中放入下载好的jar包 方法2&#xff1a;换版本&#xff0c;引入依赖 <dependency> <groupId>com.oracle.database.jdbc</groupId>…