一个故事就能够教会你看懂各种锁

news2024/11/25 4:30:20

我是一个线程,一个卖票程序的线程。

自从我们线程诞生以来,同一个进程地址空间里允许有多个执行流一起执行,效率提升的同时,也引来了很多麻烦。

我们卖票线程的工作很简单,比如票的总数是100,每卖一张就减1,直到变成0售完为止。

以前单线程的时候没啥问题,但多个线程一起执行的时候就发现,有些家伙读取到票数是100,减1后变成99,还没等他把票数写回去,另外有别的线程去读也是100,也做了同样的事,结果卖了两张票,票数才减了1张,一天下来,多卖了很多票,气的人类差点想砸了我们。

原子操作

我们把这问题反馈给了操作系统大哥,他给我们的解决方案是:读取票数->票数减1->写回票数这三个步骤不能被拆分,中途不能被打断,他说这个叫原子操作

他给了我们一套原子操作的手册,里面不止有减法,还有加法、位运算,只要调用手册里的原子操作函数,就能保证逻辑的正确性。

我很好奇操作系统大哥是如何实现这个过程的原子化的,他告诉我,如果CPU只有一个核很好办,执行原子操作的时候,他不切换线程就可以。而如果是有多个核,就需要CPU来帮忙了。

你还别说,我们用这个原子操作来卖票后,再也没有发生超额卖票的问题。

自旋锁

有一天,我们卖票程序进行了升级,不再是直接读取票数->票数减1->写回票数这么简单,还需要安排座位,现在变成了:

 我们一翻手册,没有哪个原子操作函数能满足我们的功能,毕竟安排座位这个操作是咱们卖票程序自己的事儿,一点也不通用,操作系统大哥肯定不会专门为我们开发一个原子操作函数。

我们只好再一次求助操作系统大哥,他一看就说:“你们这个问题,用自旋锁就可以解决”

锁?我们还是第一次听说这玩意,不知道是什么意思。

操作系统告诉我们,让我们回去创建一个锁,这锁里面有一个状态标记来表示当前有没有被占用,所有线程在执行卖票操作之前,都得先去获取这个锁,如果锁被占用了,线程就会阻塞在获取函数那里,获取函数内部会不断循环去检查,直到别的线程释放后才返回。

 因为获取锁的时候线程会一直循环检查状态,所以这锁也叫自旋锁。

现在,我们的工作流程变成了:

我们又可以愉快地卖票了!

互斥锁

我们的业务发展很快,后来,我们用上了数据库,把票的数量写到了数据库里面,于是我们的工作流程变成了:

 本以为只是把票数从本地内存搬到了数据库,应该没什么不一样,结果发现我们运行经常出错,还莫名其妙地被杀掉进程。

我们向操作系统大哥大倒苦水,没想到他却说:“你们还好意思诉苦,你们获取自旋锁后搞那么耗时的操作,让别的线程一直自旋等待,把CPU都跑得飞起,风扇都转个不停···”

我们都羞愧地低下了头,原来,把票数从本地内存搬到了数据库,差别这么大。

操作系统又接着说道:“自旋锁因为会使得线程一直阻塞自旋,没有让出CPU,所以只适合处理比较快速的场合,像读取数据库这种很耗时的操作,不能用它,会白白浪费CPU时间!”

我们又询问:“有没有别的不浪费CPU的办法呢?”

操作系统大哥又给我们介绍了一个叫互斥锁的东西,听说获取这个锁的时候,线程不会去自旋检查,而是把自己放到这把锁的等待队列中,然后就交出CPU执行权限,进入睡眠,看起来就跟阻塞一样,等到后面别的线程释放锁之后,再去唤醒它的等待队列里的线程继续运行。

回去以后我们就用上了互斥锁,现在我们的流程变成了这样:

 我们又又能愉快地卖票了!

条件变量

有一天,我们的卖票程序又进行了升级,21-100号票价格比较便宜,交给其他线程来卖,1-20号票价格比较贵,交给我来卖。

现在,我们不同的线程卖的票不一样了。

别的线程的流程是这样:

 而我的流程是这样:

 使用互斥锁倒也没什么问题,可就是我经常拿到锁以后发现票号还大于20,不该我处理,只好默默的释放锁,白白把我唤醒,却什么也没干!

空手而回的次数多了以后,我又去请教操作系统大哥,能不能让我指定一个条件,等条件满足了再唤醒我运行,别让我白跑。

没想到还真有办法!操作系统告诉了我一个叫条件变量的东西,等待条件变量的线程平时阻塞着,别的线程发现条件满足之后,就将条件变量激活,那个时候等待的线程才会被唤醒。

回去之后,我跟我的小伙伴儿们商量了一下,我们创建了一个条件变量,等到它们发现票号小于等于20的时候,就把条件变量激活,我就会被唤醒,再也不用白跑了!

信号量

互斥锁和条件变量真是好东西,帮了我们大忙,不仅帮我们解决了卖票的问题,我们还在其他很多地方使用它,我们遇到的绝大多数同步和互斥问题都可以用它们来解决。

直到有一天,我们遇到了一个新的问题。

我们的票越卖越好,从100到1000,票的数量越来越多,来找我们买票的客户也越来越多。

因为每次售票都要访问数据库,连接它的线程有些多,那家伙有些吃不消了。

希望我们控制一下访问数据库的线程数量。

我们很自然的想到了互斥锁,只有拿到锁的线程才能去访问数据库。

可这互斥锁名叫互斥,只能允许一个线程拿到锁,总不能只允许一个线程访问数据库吧,那可不行。所以我们希望这个名额能放宽,允许多个线程同时获得锁。

我们再一次找到了操作系统大哥,大哥拿出了他的绝招——信号量

他告诉我们,这信号量就像一个升级版的互斥锁,它里面有一个计数器,可以用来指定最多允许多少个线程同时获得它。

这正是我们想要的锁!

很快我们用上了信号量,我们又又又能愉快地卖票了!

Tips:在信号量一节中,实际上数据库能承受的并发量远不止这点,这里为了故事情节需要,弱化了数据库的并发承受能力。

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

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

相关文章

使用双指针解决问题题集(二)

1. 有效三角形的个数 给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。 示例 1: 输入: nums [2,2,3,4] 输出: 3 解释:有效的组合是: 2,3,4 (使用第一个 2) 2,3,4 (使用第二个 2) 2,2,3 示例 2: 输入: nums [4,2,3,4] 输出: 4 题解&a…

欧鹏RHCE 第四次作业

unit4.web服务的部署及高级优化方案 1. 搭建web服务器要求如下: 1.web服务器的主机ip:172.25.254.100 2.web服务器的默认访问目录为/var/www/html 默认发布内容为default‘s page 3.站点news.timinglee.org默认发布目录为/var/www/virtual/timinglee.org…

List的两种实现

前置知识: 数组 baseAddress:数组的首地址 dataTypeSize:数组中元素类型的大小,如int为4字节 为什么数组索引从0开始,假如从1开始不行吗? 在根据数组索引获取元素的时候,会用索引和寻址公式来计…

8W 1.5KVDC 隔离宽电压输入 DC/DC 电源模块——TP08DA 系列

TP08DA系列电源模块额定输出功率为8W,外形尺寸为31.80*20.30*10.00,应用于2:1,4:1电压输入范围 9-18V,18-36V,36-72V9V-36V和18V-72VDC的输入电压环境,输出电压精度可达1%,具有输出短路保护等功能&#xff…

二十、Java的反射机制

1、Java反射机制的概念 所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。如下: 正常方式:引入需要的“包.类”名称---->通过new实例化----->取得实例化对象。 反射方式:实例化对象---->getClass()方法------>得到完整的“包.类”名…

鸿蒙通用组件弹窗简介

鸿蒙通用组件弹窗简介 弹窗----Toast引入ohos.promptAction模块通过点击按钮,模拟弹窗 警告对话框----AlertDialog列表弹窗----ActionSheet选择器弹窗自定义弹窗使用CustomDialog声明一个自定义弹窗在需要使用的地方声明自定义弹窗,完整代码 弹窗----Toa…

JavaSE——异常(1/2)-认识异常(介绍,异常的体系,了解如何解决异常)、自定义异常(自定义异常的种类,实例演示)

目录 认识异常 介绍 异常的体系 了解如何解决异常 自定义异常 自定义异常的种类 实例演示 认识异常 介绍 什么是异常? 异常就是代表程序出现的问题。 比如: int[] arr {10,20,30}; System.out.println(arr[3]);System.out.println(10 / 0); …

Vince9120雅思小作文笔记——P1 Intro(前言)

文章目录 链接P1 Intro(前言)字数限制题型综述(problem types overview)1. **柱状图(Bar Chart)** - 描述不同类别在某个或多个变量上的数据量比较。2. **线图(Line Graph)** - 展示…

项目经理【过程】概念

系列文章目录 【引论一】项目管理的意义 【引论二】项目管理的逻辑 【环境】概述 【环境】原则 【环境】任务 【环境】绩效 【人】概述 【人】原则 【人】任务 【人】绩效 【过程】概念 一、过程是什么 1.1 项目管理五大过程组 1.2 五大过程组之间的相互作用 1.3 项目阶段VS过…

Pytorch学习笔记——神经网络基本框架

一、神经网络是什么 神经网络在人工智能和深度学习的领域,一般称为人工神经网络,即ANN(Artificial Neural Network),是一种模仿人脑神经系统工作方式的计算模型。被广泛应用于人工智能、自动控制、机器人、统计学等领域的信息处理中。 二、…

推荐一个gpt全能网站

进入后,里面是这样的 点开后,里面是这样的 你以为只有这些吗? 往下翻一翻,你会发现新大陆!! 在输入框的下面,有一个分类栏,鼠标移上去,下面就会给出一堆网站 光是gp…

Git === Git概述 Git安装

第1章 Git概述 Git是一个免费的、开源的分布式版本控制系统,可以快速高效地处理从小型到大型的各种项目。 Git易于学习,占地面积小,性能极快。 它具有廉价的本地库,方便的暂存区域和多个工作流分支等特性。其性能优于Subversion…

未授权访问:Redis未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、利用redis未授权写入weshell 5、利用redis未授权反弹shell 6、利用redis未授权实现免密登录 防御手段 从这篇文章开始我就要开始学习各种未授权访问的知识和相关的实操实验了,一共有好多篇,内容主…

五分钟解决Springboot整合Mybaties

SpringBoot整合Mybaties 创建maven工程整合mybaties逆向代码生成 创建maven工程 1.通过idea创建maven工程如下图 2.生成的工程如下 以上我们就完成了一个maven工程,接下来我们改造成springboot项目。 这里主要分为三步:添加依赖,增加配置&…

二叉搜索树相关

二叉搜索树 定义:对二叉搜索树的一些操作基本结构Insert操作Find操作Erase操作 InOrder遍历二叉树操作模拟字典模拟统计次数 定义: 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:若它的左子树不为空&a…

《Decoupled Contrastive Learning for Long-Tailed Recognition》阅读笔记

论文标题 《Decoupled Contrastive Learning for Long-Tailed Recognition》 针对长尾识别的解耦对比学习 作者 Shiyu Xuan 和 Shiliang Zhang 来自北京大学计算机学院多媒体信息处理国家重点实验室 初读 摘要 监督对比损失(Supervised Contrastive Loss, SC…

LeetCode //C - 85. Maximal Rectangle

85. Maximal Rectangle Given a rows x cols binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area. Example 1: Input: matrix [[“1”,“0”,“1”,“0”,“0”],[“1”,“0”,“1”,“1”,“1”],[“1”,“…

Netty HTTP 示例-响应式编程-013

🤗 ApiHug {Postman|Swagger|Api...} = 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱,有温度,有质量,有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace The Next Generation API Development Platform…

The provided password or token is incorrect or your account

IDEA使用git技巧 【/n】 01 问题出现场景 我的gitlab上个月生成的token到期了,于是今天推上去的时候报了这个错误 The provided password or token is incorrect or your account has 2FA enabled and you must use a personal access token instead of a password. See ht…

【知识碎片】2024_05_07

今天记录了两个代码和C的几个破碎知识。 第一段代码是基础型的,关于数组。第二段代码是二分的,一开始没通过全部案例,值得再看。 每日代码 1.记负均正 输入一个数组,输出负数的个数,整数的平均值(0都不参…