当前位置: 首页 > news >正文

多线程 - 锁策略 CAS

v2-4b5a5ad9fde3b92509faea52047f65bc_b

常见的锁策略

此处谈到的锁策略,不局限于 Java,C++,Python,数据库,操作系统……但凡是涉及到锁,都是可以应用到下列的锁策略的

乐观锁 vs 悲观锁

锁的实现者,预测接下来锁冲突(锁竞争,两个线程针对一个对象加锁,产生阻塞等待了)的概率是大,还是不大,根据这个冲突的概率,来接下来做什么
~~ 这不是两把具体的锁,而是“两类锁”.

悲观锁:预测锁竞争不是很激烈.
乐观锁:预测锁竞争会很激烈.

通常来说,悲观锁一般做的工作更多一些,效率更低一些,乐观锁做的工作会更少一些,效率更高一点.但是并不绝对.
悲观锁和乐观锁,唯一的区别,主要就是看预测锁竞争激烈程度的结论.

轻量级锁 vs 重量级锁

轻量级锁: 加锁解锁,过程更快更高效.
重量级锁: 加锁解锁,过程更慢,更低效.

一个乐观锁很可能也是一个轻量级锁,一个悲观锁很可能也是一个重量级锁.
多数情况下,乐观锁,也是一个轻量级锁.
多数情况下,悲观锁也是一个重量级锁.

自旋锁 vs 挂起等待锁

自旋锁是轻量级锁的一种典型实现.
挂起等待锁是重量级锁的一种典型实现.

image-20231010185737617

互斥锁 vs 读写锁

互斥锁

synchronized,是互斥锁
synchronized 只有两个操作:
1.进入代码块,加锁
2.出了代码块,解锁
加锁,就只是单纯的加锁,没有更细化的区分了

读写锁
~~ 读写锁,能够把读和写两种加锁区分开

读写锁:
1.给读加锁
2.给写加锁
3.解锁
注: 如果多个线程读同一个变量,不会涉及到线程安全问题!!!

读写锁中,约定:
1.读锁和读锁之间,不会锁竞争.不会产生阻塞等待,不会影响程序的速度,代码执行很快.
2.写锁和写锁之间,有锁竞争,减慢速度,但是保证准确性.
3.读锁和写锁之间,也有锁竞争,减慢速度,但是保证准确性.
注:
1.非必要不加锁.
2.读写锁更适合于,一写多读的情况.
3.多线程针对同一个变量并发读,这时是没有线程安全问题的,也就不需要加锁控制.
4.很多开发场景中,读操作非常高频,比写操作的频率高很多.
5.在Java标准库里面也提供了读写锁的具体实现(两个类,读锁类,写锁类).

公平锁 vs 非公平锁

此处把公平定义成“先来后到”
image-20231010214216578

公平锁: 当女神分手之后,就由等待队列中,最早来的舔狗上位.

image-20231010220437659

非公平锁: 雨露均沾了.
image-20231011011841261

注: 操作系统和 Java synchronized 原生都是“非公平锁”,操作系统这里的针对加锁的控制,本身就依赖于线程调度顺序.这个调度顺序是随机的,不会考虑到这个线程等待锁多久了.
要想实现公平锁,就得在这个基础上,能够引入额外的东西(引入一个队列,让这些加锁的线程去排队).

可重入锁 vs 不可重入锁

不可重入锁: 一个线程针对一把锁,连续加锁两次,出现死锁.
可重入锁: 一个线程针对一把锁,连续加锁多次都不会死锁.
注: 系统原生的锁,C++标准库的锁,Python标准库的锁…都不是可重入的锁!
synchronized是个"可重入锁",(加锁的时候会判定一下,看当前尝试申请锁的线程是不是已经就是锁的拥有者了,如果是,直接放行)

synchronized锁

针对上述六组锁策略, synchronized这把锁属于哪种呢??

synchronized 既是悲观锁,也是乐观锁 ~~ synchronized会根据当前锁竞争的激烈程度,自适应.
既是轻量级锁,也是重量级锁 ~~ synchronized默认是轻量级锁,如果发现当前锁竞争比较激烈,就会转换成重量级锁.
synchronized这里的轻量级锁部分基于自旋锁的方式实现,synchronized这里的重量级锁部分基于挂起等待锁的方式实现.
synchronized不是读写锁.
synchronized是非公平锁.
synchronized是可重入锁.
总结: 上述谈到的六种锁策略,可以视为是“锁的形容词”.

CAS

CAS ~~ 全称Compare and swap, 字面意思:”比较并交换“
一个 CAS 涉及到以下操作 :

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。
1.比较 A 与 V 是否相等。(比较)
2.如果比较相等,将 B 写入 V。(交换)
3.返回操作是否成功

image-20231011162715937

此处最特别的地方,上述这个 CAS 的过程,并非是通过一段代码实现的,而是通过一条 CPU 指令完成的 => CAS 操作是原子的 ~~ 就可以在一定程度上回避线程安全问题
因此解决线程安全问题除了加锁之外,又有了一个新的方向了.
小结: CAS 可以理解成是 CPU 给我们提供的一个特殊指令,通过这个指令,就可以一定程度的处理线程安全问题.

CAS 伪代码

image-20231011164911903

注: 下面写的代码不是原子的, 真实的 CAS 是一个原子的硬件指令完成的. 这个伪代码只是辅助理解 CAS 的工作流程.

CAS 的应用场景

1.实现原子类(Java 标准库里提供的类)

标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.典型的就是 AtomicInteger 类,其中的 getAndIncrement 相当于 i++ 操作.

import java.util.concurrent.atomic.AtomicInteger;/*** Created with IntelliJ IDEA.* Description:* User: fly(逐梦者)* Date: 2023-10-09* Time: 10:49*/
public class ThreadDemo28 {public static void main(String[] args) throws InterruptedException {// 这些原子类,就是基于 CAS 实现了 自增,自减等操作.此时进行这类操作不需要加锁,也是线程安全的.AtomicInteger count = new AtomicInteger(0);// 使用原子类, 来解决线程安全问题Thread t1 = new Thread(() -> {for (int i = 0; i < 5_0000; i++) {// 因为 java 不支持运算符重载,所以只能使用普通方法来表示自增自减count.getAndIncrement();// count++//count.incrementAndGet(); => ++ count//count.getAndDecrement(); => count--//count.decrementAndGet(); => -- count}});Thread t2 = new Thread(() -> {for (int i = 0; i < 5_0000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get());}
}

伪代码实现AtomicInteger

image-20231011235536304

2.实现自旋锁

自旋锁伪代码

image-20231012094749343

注: CAS 属于“特殊方法”,synchronized 属于“通用方法” –> 各种场景,都能使用(打击面广)

CAS 的典型问题: ABA 问题

CAS 在运行中的核心,检查 value 和 oldValue 是否一致.如果一致,就视为value 中途没有被修改过,所以进行下一步交换操作是没问题的.

这里一致,可能是没改过.也可能是,改过,但是还原回来了?!
把 value 的值设为A的话, CAS 判定 value 为 A,此时可能确实value始终是A,也可能是value本来是A,被改成了B,又还原成了A …

ABA 问题就相当于,买个手机,买到的这个手机,可能是新机,也可能是翻新机.

翻新机: 二手的,被销售商回收了,经过一些翻新操作(把外壳换了,重新包装).

ABA 这个情况,大部分情况下,其实是不会对代码/逻辑产生太大影响的,但是不排除一些“极端情况”,也是可能造成影响的.
例子:
image-20231012154239605

上述场景,概率非常低!!!一方面,恰好,滑稽这边多按了几次,产生多个扣款动作了,另一方面,恰好在这个非常极限的时间内,有人转账了一样的金额.

解决方案

针对当前问题,采取的方案,就是加入一个版本号.想象成,初始版本号是1,每次修改版本号都+1,然后进行 CAS 的时候不是以金额为基准了,而是以版本号为基准.此时,版本号要是没变,就是一定没有发生改变(版本号是只能增长,不能降低的).

相关文章:

多线程 - 锁策略 CAS

常见的锁策略 此处谈到的锁策略,不局限于 Java,C,Python,数据库,操作系统……但凡是涉及到锁,都是可以应用到下列的锁策略的 乐观锁 vs 悲观锁 锁的实现者,预测接下来锁冲突(锁竞争,两个线程针对一个对象加锁,产生阻塞等待了)的概率是大,还是不大,根据这个冲突的概率,来接下…...

VP记录——The 2021 CCPC Weihai Onsite

网址 2021CCPC威海 赛时过题与罚时 A.Goodbye, Ziyin! 签到题&#xff0c;队友写的 #include<bits/stdc.h> using namespace std; int cnt[10], de[1000010]; int main() {int n;cin >> n;for(int i 1; i < n; i) {int u, v;scanf("%d %d", &…...

JavaWeb---Servlet

1.Srvlet概述 Servlet是运行在java服务器端的程序&#xff0c;用于接收和响应来着客户端基于HTTP协议的请求 如果想实现Servlet的功能&#xff0c;可以通过实现javax。servlet。Servlet接口或者继承它的实现类 核心方法&#xff1a;service&#xff08;&#xff09;&#xf…...

英语——方法篇——单词——谐音法+拼音法——50个单词记忆

theatre&#xff0c;剧场&#xff0c;太后th吃eat热re食物&#xff0c;就去剧场了 loud dolphin&#xff0c;做do脸皮厚plh在。。。里 humid&#xff0c;hu湖mi米d的 blender&#xff0c;b爸lend借给er儿。 tragedy&#xff0c;tr土人...

35道Rust面试题

这套Rust面试题包括了填空题、判断题、连线题和编码题等题型。 选择题 1 &#xff0c;下面哪个是打印变量language的正确方法&#xff1f; A&#xff0c;println("{}", language); B&#xff0c;println(language); C&#xff0c;println!("{}", langu…...

01 时钟配置初始化,debug

1. 开启debug series&#xff0c;否则只能下载一次&#xff0c;再次下载要配置boot 2.f0外部时钟配置 h750 配置 实测可用...

Halcon我的基础教程(一)(我的菜鸟教程笔记)-halcon仿射变换(Affine Transformation)的探究与学习

目录 什么是仿射变换?仿射变换有哪些方式?任何仿射变换都能由以下基本变换构造而来:在Halocn中,仿射变换具有重要的作用,那我们本文章重点讨论仿射变换基础性知识。 使用Halcon中的重要算子,仿射变换一般解决步骤,案例应用会在以后的文章中我们重点解答与讨论。 我们首先…...

c++视觉---中值滤波处理

中值滤波&#xff08;Median Filter&#xff09;是一种常用的非线性平滑滤波方法&#xff0c;用于去除图像中的噪声。它不像线性滤波&#xff08;如均值滤波或高斯滤波&#xff09;那样使用权重来计算平均值或加权平均值&#xff0c;而是选择滤波窗口内的像素值中的中间值作为输…...

Edge使用猴油脚本实战(实验室安全考试系统刷在线时长——网站永久自动刷新)

介绍 篡改猴 (Tampermonkey) 是拥有 超过 1000 万用户 的最流行的浏览器扩展之一。它允许用户自定义并增强您最喜爱的网页的功能。用户脚本是小型 JavaScript 程序&#xff0c;可用于向网页添加新功能或修改现有功能。使用 篡改猴&#xff0c;您可以轻松在任何网站上创建、管理…...

Vue 中 KeepAlive 内置缓存使用

KeepAlive 介绍及使用场景 KeepAlive 是 vue 中的内置组件&#xff0c;当多个组件动态切换时可以对实例状态进行缓存&#xff0c;用法如下 <router-view v-slot"{ Component }"><keep-alive><component :is"Component" /></keep-al…...

语言模型编码中/英文句子格式详解

文章目录 前言一、Bert的vocab.txt内容查看二、BERT模型转换方法(vocab.txt)三、vocab内容与模型转换对比四、中文编码总结 前言 最近一直在学习多模态大模型相关内容&#xff0c;特别是图像CV与语言LLM模型融合方法&#xff0c;如llama-1.5、blip、meta-transformer、glm等大…...

【Node.js】路由

基础使用 写法一&#xff1a; // server.js const http require(http); const fs require(fs); const route require(./route) http.createServer(function (req, res) {const myURL new URL(req.url, http://127.0.0.1)route(res, myURL.pathname)res.end() }).listen…...

matlab 2ask 4ask 信号调制

1 matlab 2ask close all clear all clcL =1000;Rb=2822400;%码元速率 Fs =Rb*8; Fc=Rb*30;%载波频率 Ld =L*Fs/Rb;%产生载波信号 t =0:1/Fs:L/Rb;carrier&...

Python利用jieba分词提取字符串中的省市区(字符串无规则)

目录 背景库&#xff08;jieba&#xff09;代码拓展结尾 背景 今天的需求就是在一串字符串中提取包含&#xff0c;省、市、区&#xff0c;该字符串不是一个正常的地址;,如下字符串 "安徽省、浙江省、江苏省、上海市,冷运标快首重1kg价格xx元,1.01kg(含)-5kg(不含)续重价…...

MuLogin防关联浏览器帮您一键实现Facebook账号多开

导言&#xff1a; 在当今数字化时代&#xff0c;社交媒体应用程序的普及程度越来越高。Facebook作为全球最大的社交媒体平台之一&#xff0c;拥有数十亿的用户。然而&#xff0c;对于一些用户来说&#xff0c;只拥有一个Facebook账号可能无法满足他们的需求。有时&#xff0c;…...

【C语言】每日一题(半月斩)——day4

目录 选择题 1、设变量已正确定义&#xff0c;以下不能统计出一行中输入字符个数&#xff08;不包含回车符&#xff09;的程序段是&#xff08; &#xff09; 2、运行以下程序后&#xff0c;如果从键盘上输入 65 14<回车> &#xff0c;则输出结果为&#xff08; &…...

Are you sure you want to continue connecting (yes/no) 每次ssh进

Lunix scp等命令不需要输入yes确认方法_scp不需要确认-CSDN博客 方法一&#xff1a;连接时加入StrictHostKeyCheckingno ssh -o StrictHostKeyCheckingno root192.168.1.100 方法二&#xff1a;修改/etc/ssh/ssh_config配置文件&#xff0c;添加&#xff1a; StrictHostKeyC…...

网络与信息系统安全设计规范

1、总则 1.1、目的 为规范XXXXX单位信息系统安全设计过程&#xff0c;确保整个信息安全管理体系在信息安全设计阶段符合国家相关标准和要求&#xff0c;特制订本规范。 1.2、范围 本规范适用于XXXXX单位在信息安全设计阶段的要求和规范管理。 1.3、职责 网络安全与信息化…...

在Linux怎么用vim实现把一个文件里面的文本复制到另一个文件里面

2023年10月9日&#xff0c;周一下午 我昨天遇到了这个问题&#xff0c;但在网上没找到图文并茂的博客&#xff0c;于是我自己摸索出解决办法后&#xff0c;决定写一篇图文并茂的博客。 情景 假设现在我要用vim把file_transfer.cpp的内容复制到file_transfer.hpp里面 第一步 …...

CCAK—云审计知识证书学习

目录 一、CCAK云审计知识证书概述 二、云治理概述 三、云信任 四、构建云合规计划 <...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战

“&#x1f916;手搓TuyaAI语音指令 &#x1f60d;秒变表情包大师&#xff0c;让萌系Otto机器人&#x1f525;玩出智能新花样&#xff01;开整&#xff01;” &#x1f916; Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制&#xff08;TuyaAI…...

Go 语言并发编程基础:无缓冲与有缓冲通道

在上一章节中&#xff0c;我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道&#xff0c;它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好&#xff0…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

离线语音识别方案分析

随着人工智能技术的不断发展&#xff0c;语音识别技术也得到了广泛的应用&#xff0c;从智能家居到车载系统&#xff0c;语音识别正在改变我们与设备的交互方式。尤其是离线语音识别&#xff0c;由于其在没有网络连接的情况下仍然能提供稳定、准确的语音处理能力&#xff0c;广…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

【实施指南】Android客户端HTTPS双向认证实施指南

&#x1f510; 一、所需准备材料 证书文件&#xff08;6类核心文件&#xff09; 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...

Spring是如何实现无代理对象的循环依赖

无代理对象的循环依赖 什么是循环依赖解决方案实现方式测试验证 引入代理对象的影响创建代理对象问题分析 源码见&#xff1a;mini-spring 什么是循环依赖 循环依赖是指在对象创建过程中&#xff0c;两个或多个对象相互依赖&#xff0c;导致创建过程陷入死循环。以下通过一个简…...

python打卡day47

昨天代码中注意力热图的部分顺移至今天 知识点回顾&#xff1a; 热力图 作业&#xff1a;对比不同卷积层热图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import D…...