乐观锁,CAS,ABA问题,synchronized锁升级过程

常见的锁策略


乐观锁 vs 悲观锁


乐观锁:乐观锁假设认为数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则返回用户错误的信息,让用户决定如何去做。
悲观锁的问题:总是需要竞争锁,进而导致发生线程切换,挂起其他线程;所以性能不高。

悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
乐观锁的问题:并不总是能处理所有问题,所以会引入一定的系统复杂度。

读写锁


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

自旋锁(Spin Lock)


在线程在抢锁失败后会进入阻塞状态,放弃 CPU,需要过很久才能再次被调度。但经过测算,实际的生活中,大部分情况下,虽然当前抢锁失败,但过不了很久,锁就会被释放。基于这个事实,自旋锁诞生了。
你可以简单的认为自旋锁就是下面的代码只要没抢到锁,就死等,不断地进行申请锁。

自旋锁的缺点:缺点其实非常明显,就是如果之前的假设(锁很快会被释放)没有满足,则线程其实是光在消耗 CPU 资源,长期在做无用功的。

可重入锁


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

什么是 CAS


CAS: 全称Compare and swap,字面意思:”比较并交换“, CAS 其实是一个乐观锁。一个 CAS 涉及到以下操作:
我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

  1. 比较 A 与 V 是否相等。(比较)
  2. 如比较相等,将 B 写入 V。(交换)
  3. 返回操作是否成功。


当多个线程同时对某个资源进行CAS操作,只能有一个线程操作成功,但是并不会阻塞其他线程,其他线程只会收到操作失败的信号。即第二步返回的为false,然后就进入自旋状态,将当前内存的值赋值给A(预期值),在从重复执行上面的步骤。

CAS 是怎么实现的


针对不同的操作系统,JVM 用到了不同的 CAS 实现原理,简单来讲:
java 的 CAS 利用的的是 unsafe 这个类(本地类)提供的 CAS 操作;unsafe 的 CAS 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg(原子指令);Atomic::cmpxchg 的实现使用了汇编的 CAS 操作,并使用 cpu 硬件提供的 lock 机制保证其原子性。java中atomic*相关的类都是依靠CAS实现的。比如atomicInteger类atomicLong等。
简而言之,是因为硬件予以了支持,软件层面才能做到。

ABA 问题(乐观锁导致的)


在上面的CAS中,原数据V的值从C变成了D又变成了C,而这个期间我们不清楚这个过程,误认为这个值没有改变。在进行写入的时候,期望值A与原数据V的值相同,就直接进行了写入,返回操作成功。但这是不正确的,举个例子:

比如你的银行卡里有1000块钱,你个朋友要借500,这时你在转账的时候不下心点了两次。二者期间恰好有人给你转了500块。我们用CAS来分析一下这个示例。原数V(1000),旧的预期值A(1000),需要修改的新值B(500),当你操作后你的卡里就只有500了,而第二次误按的就不会操作成功(因为这时的V为500不等于A1000)。而恰好别人给你转账500后你的卡里就又是1000了,而你不小心多按的一次就也会执行成功了。这就是一个ABA问题了。

解决方法:加入版本信息,例如携带 AtomicStampedReference 之类的时间戳作为版本信息,保证不会出现老的值。每次修改的时候判断预期的旧值和版本号,每次成功修改之后更改版本号,这样即使预期的值和V值相等,但因为版本号不同,所以也不能进行改,从而解决了ABA 的问题。AtomicStampedReference类就通过版本号解决了ABA问题。

synchronized 锁


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

无锁

没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功,其他修改失败的线程会不断重试直到修改成功。

偏向锁

对象的代码一直被同一线程执行,不存在多个线程竞争,该线程在后续的执行中自动获取锁,降低获取锁带来的性能开销。偏向锁,指的就是偏向第一个加锁线程,该线程是不会主动释放偏向锁的,只有当其他线程尝试竞争偏向锁才会被释放。偏向锁的撤销,需要在某个时间点上没有字节码正在执行时,先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁;如果线程处于活动状态,升级为轻量级锁的状态。

轻量级锁

轻量级锁是指当锁是偏向锁的时候,被第二个线程 B 所访问,此时偏向锁就会升级为轻量级锁,线程 B会通过自旋的形式尝试获取锁,线程不会阻塞,从而提高性能。当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁;当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁。

重量级锁

指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。

synchronized锁的变化流程图

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

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

相关文章

FPGA_ip_Rom

一 理论 Rom存储类ip核,Rom是只读存储器的简称,是一种只能读出事先存储数据的固态半导体存储器。 特性: 一旦储存资料,就无法再将之改变或者删除,且资料不会因为电源关闭而消失。 单端口Rom: 双端口rom: 二 Rom ip核…

精品基于Spring boot的某小区物业缴费报修管理系统

《[含文档PPT源码等]精品基于Spring boot的某小区物业管理系统[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功! 软件开发环境及开发工具: Java——涉及技术: 前端使用技术:H…

MongoDB之概述、命令

基础知识 是什么 概念 分布式文件存储数据库,提供高可用、可扩展、易部署的数据存储解决方案。 结构 BSON存储类型 类似JSON的一种二进制存储格式。相比于JSON,提供更丰富的类型支持。 优点是灵活,缺点是空间利用率不佳。 类型说明解释…

创建菜单与游戏页面

bootstrap地址 Bootstrap v5 中文文档 Bootstrap 是全球最受欢迎的 HTML、CSS 和 JS 前端工具库。 | Bootstrap 中文网 (bootcss.com) 创建导航栏组件 web--src--components--NavBar.vue <!-- html --> <template><nav class"navbar navbar-expand-lg n…

机器学习:Softmax介绍及代码实现

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于…

GO语言笔记4-标识符、关键字与运算符

标识符 什么是标识符 变量名、方法名等我们起的名字都是标识符 标识符定义规则 字母、数字、下划线组成不可以数字开头&#xff0c;严格区分大小写&#xff0c;不能带有空格&#xff0c;不可以是go的关键字不能单独使用 下划线&#xff0c;因为下划线在GO中是一个特殊标识符&…

2024刘谦春晚第二个扑克牌魔术

前言 就是刚才看春晚感觉这个很神奇&#xff0c;虽然第一个咱模仿不过来&#xff0c;第二个全国人民这么多人&#xff0c;包括全场观众都有成功&#xff0c;这肯定是不需要什么技术&#xff0c;那我觉得这个肯定就是数学了&#xff0c;于是我就胡乱分析一通。 正文 首先准备…

【蓝桥杯冲冲冲】k 短路 / [SDOI2010] 魔法猪学院

蓝桥杯备赛 | 洛谷做题打卡day33 文章目录 蓝桥杯备赛 | 洛谷做题打卡day33题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示数据规模数据更新日志 题解代码我的一些话 【模板】k 短路 / [SDOI2010] 魔法猪学院 题目背景 注&#xff1a;对于 k k k 短路问…

正则可视化工具:学习和编写正则表达式的利器

引言 正则表达式是一种强大的文本匹配和处理工具&#xff0c;但对于初学者和非专业开发者来说&#xff0c;编写和理解正则表达式可能是一项具有挑战性的任务。为了帮助人们更好地学习和编写正则表达式&#xff0c;正则可视化工具应运而生。本文将探讨正则可视化工具的优点&…

并行计算导论 笔记 1

目录 并行编程平台隐式并行超标量执行/指令流水线超长指令字处理器 VLIW 内存性能系统的局限避免内存延迟的方法 并行计算平台控制结构通信模型共享地址空间平台消息传递平台对比 物理组织理想并行计算机并行计算机互联网络网络拓朴结构基于总线的网络交叉开关网络多级网络全连…

leaflet 显示自己geoserver发布的中国地图

安装vscode 安装 通义灵码 问题&#xff1a; 用leaflet显示一个wms地图 修改下代码&#xff0c;结果如下&#xff1a; 例子代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport&q…

自然语言处理(NLP)—— 基本概念

自然语言处理&#xff08;Natural Language Processing&#xff0c;简称NLP&#xff09;是人工智能和语言学领域的一个分支&#xff0c;它涉及到计算机和人类&#xff08;自然&#xff09;语言之间的相互作用。它的主要目标是让计算机能够理解、解释和生成人类语言的数据。NLP结…

极狐GitLab 与钉钉的集成实践

DingTalk OAuth 2.0 OmniAuth provider * 引入于 14.5 版本。 您可以使用您的钉钉账号登录极狐GitLab。 登录钉钉开放平台&#xff0c;创建应用。钉钉会生成一个客户端 ID 和密钥供您使用。 登录钉钉开放平台。 在顶部栏上&#xff0c;选择 应用程序开发 > 企业内部开发&am…

【EAI 015】CLIPort: What and Where Pathways for Robotic Manipulation

论文标题&#xff1a;CLIPort: What and Where Pathways for Robotic Manipulation 论文作者&#xff1a;Mohit Shridhar1, Lucas Manuelli, Dieter Fox1 作者单位&#xff1a;University of Washington, NVIDIA 论文原文&#xff1a;https://arxiv.org/abs/2109.12098 论文出处…

【数据结构】链式队列解析(C语言版)

数据结构——链队列解析过程和简单代码实现&#xff1a; 一、简单概念&#xff1a; 动图展示&#xff1a; (1)入队&#xff1a;(2)出队&#xff1a; 二、顺序队列&#xff1a; 思路步奏&#xff1a; &#xff08;1&#xff09;入队操作&#xff1a;&#xff08;2&#xff09;出…

《统计学简易速速上手小册》第6章:多变量数据分析(2024 最新版)

文章目录 6.1 主成分分析&#xff08;PCA&#xff09;6.1.1 基础知识6.1.2 主要案例&#xff1a;客户细分6.1.3 拓展案例 1&#xff1a;面部识别6.1.4 拓展案例 2&#xff1a;基因数据分析 6.2 聚类分析6.2.1 基础知识6.2.2 主要案例&#xff1a;市场细分6.2.3 拓展案例 1&…

第2讲投票系统后端架构搭建

创建项目时&#xff0c;随机选择一个&#xff0c;后面会生成配置properties文件 生成文件 maven-3.3.3 设置阿里云镜像 <?xml version"1.0" encoding"UTF-8"?><!-- Licensed to the Apache Software Foundation (ASF) under one or more cont…

【Python】Mac 本地部署 stable-diffusion

其实要在本地部署 stable-diffusion 不难&#xff0c;只要有“魔法”一切都水到渠成&#xff0c;如下图&#xff1a; (base) MacBook-Pro python % git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui Cloning into stable-diffusion-webui... remote: Enu…

前端JavaScript篇之setTimeout、Promise、Async/Await 的区别

目录 setTimeout、Promise、Async/Await 的区别**setTimeout**:思路需要注意的 **Promise**:思路需要注意的 **Async/Await**:思路需要注意的 总结 setTimeout、Promise、Async/Await 的区别 setTimeout: 概念&#xff1a;setTimeout是JavaScript中的一个函数&#xff0c;用于…

c++求三个数中最大数

#include<iostream> using namespace std; int main() { int a,b,c; cout<<"请输入三个数字"<<endl;//end后面为小写的L cin>>a>>b>>c; if(a>b&&a>c) cout<<"最大数为a:"<<a<<e…